1
0

fix: Add type reference info and remove query paged.

This commit is contained in:
Ca2didi 2025-03-30 20:58:15 +08:00
parent d2041ea5e4
commit 621d9f4318
15 changed files with 75 additions and 110 deletions

View File

@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeEditing/SuppressUninitializedWarningFix/Enabled/@EntryValue">False</s:Boolean></wpf:ResourceDictionary>

View File

@ -1,17 +0,0 @@
namespace Flawless.Communication.Request;
public class QueryPagesRequest<T>
{
public required int Offset { get; init; }
public required int Length { get; init; }
public T? Parameter { get; init; }
}
public class QueryPagesRequest
{
public required int Offset { get; init; }
public required int Length { get; init; }
}

View File

@ -0,0 +1,3 @@
namespace Flawless.Communication.Response;
public record CommitSuccessResponse(DateTime CommittedOn, Guid CommitId);

View File

@ -0,0 +1,3 @@
namespace Flawless.Communication.Response;
public record ListingResponse<T>(T[] Result);

View File

@ -1,25 +0,0 @@
namespace Flawless.Communication.Response;
public record PagedResponse<T>
{
public required int Offset { get; init; }
public required int Length { get; init; }
public int? Total { get; init; }
public IEnumerable<T>? Data { get; init; }
}
public record PagedResponse<TData, TMetadata>
{
public required int Offset { get; init; }
public required int Length { get; init; }
public int? Total { get; init; }
public IEnumerable<TData>? Data { get; init; }
public TMetadata? Metadata { get; init; }
}

View File

@ -0,0 +1,3 @@
namespace Flawless.Communication.Response;
public record PeekResponse<T>(T Result);

View File

@ -0,0 +1,4 @@
namespace Flawless.Communication.Response;
public record RepositoryCommitResponse(
Guid Id, string Author, DateTime CommitedOn, string Message, Guid MainDepotId);

View File

@ -2,5 +2,7 @@
public record ServerStatusResponse public record ServerStatusResponse
{ {
public required string? FriendlyName { get; init; }
public required bool AllowPublicRegister { get; set; } public required bool AllowPublicRegister { get; set; }
} }

View File

@ -0,0 +1,3 @@
namespace Flawless.Communication.Shared;
public record LockFileInfo(string Path, string Owner);

View File

@ -27,7 +27,8 @@ public class AuthenticationController(
{ {
return Task.FromResult<ActionResult<ServerStatusResponse>>(Ok(new ServerStatusResponse() return Task.FromResult<ActionResult<ServerStatusResponse>>(Ok(new ServerStatusResponse()
{ {
AllowPublicRegister = true AllowPublicRegister = true,
FriendlyName = "Ca2didi Server"
})); }));
} }

View File

@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.OpenApi.Validations.Rules;
namespace Flawless.Server.Controllers; namespace Flawless.Server.Controllers;
@ -108,7 +109,7 @@ public class RepositoryInnieController(
} }
[HttpGet("get_users")] [HttpGet("get_users")]
public async Task<IActionResult> GetUsersAsync(string repositoryName, QueryPagesRequest r) public async Task<ActionResult<ListingResponse<RepoUserRole>>> GetUsersAsync(string repositoryName)
{ {
if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3) if (string.IsNullOrWhiteSpace(repositoryName) || repositoryName.Length <= 3)
return BadRequest(new FailedResponse("Repository name is empty or too short!")); return BadRequest(new FailedResponse("Repository name is empty or too short!"));
@ -122,17 +123,11 @@ public class RepositoryInnieController(
if (rp == null) return BadRequest(new FailedResponse("Repository not found!")); if (rp == null) return BadRequest(new FailedResponse("Repository not found!"));
return Ok(new PagedResponse<RepoUserRole> return Ok(new ListingResponse<RepoUserRole>(rp.Members.Select(pm => new RepoUserRole
{ {
Length = r.Length, Username = pm.User.UserName!,
Offset = r.Offset, Role = pm.Role
Total = rp.Members.Count, }).ToArray()));
Data = rp.Members.Select(pm => new RepoUserRole
{
Username = pm.User.UserName!,
Role = pm.Role
})
});
} }
[HttpPost("update_user")] [HttpPost("update_user")]
@ -230,6 +225,7 @@ public class RepositoryInnieController(
[HttpGet("fetch_manifest")] [HttpGet("fetch_manifest")]
[ProducesResponseType<FileStreamResult>(200)]
public async Task<IActionResult> DownloadManifestAsync(string userName, string repositoryName, [FromQuery] string commitId) public async Task<IActionResult> DownloadManifestAsync(string userName, string repositoryName, [FromQuery] string commitId)
{ {
if (!Guid.TryParse(commitId, out var commitGuid)) return BadRequest(new FailedResponse("Invalid commit id")); if (!Guid.TryParse(commitId, out var commitGuid)) return BadRequest(new FailedResponse("Invalid commit id"));
@ -245,6 +241,7 @@ public class RepositoryInnieController(
[HttpGet("fetch_depot")] [HttpGet("fetch_depot")]
[ProducesResponseType<FileStreamResult>(200)]
public async Task<IActionResult> DownloadDepotAsync(string userName, string repositoryName, [FromQuery] string depotId) public async Task<IActionResult> DownloadDepotAsync(string userName, string repositoryName, [FromQuery] string depotId)
{ {
if (!Guid.TryParse(depotId, out var depotGuid)) return BadRequest(new FailedResponse("Invalid depot id")); if (!Guid.TryParse(depotId, out var depotGuid)) return BadRequest(new FailedResponse("Invalid depot id"));
@ -260,50 +257,49 @@ public class RepositoryInnieController(
} }
[HttpGet("list_commit")] [HttpGet("list_commit")]
public async Task<IActionResult> ListCommitsAsync(string userName, string repositoryName) public async Task<ActionResult<ListingResponse<RepositoryCommitResponse>>> ListCommitsAsync(string userName, string repositoryName)
{ {
var user = (await userManager.GetUserAsync(HttpContext.User))!; var user = (await userManager.GetUserAsync(HttpContext.User))!;
var grantIssue = await ValidateRepositoryAsync(userName, repositoryName, user, RepositoryRole.Guest); var grantIssue = await ValidateRepositoryAsync(userName, repositoryName, user, RepositoryRole.Guest);
if (grantIssue is not Repository) return (IActionResult) grantIssue; if (grantIssue is not Repository) return (ActionResult) grantIssue;
var commit = await dbContext.Repositories var commit = dbContext.Repositories
.Where(r => r.Owner.UserName == userName && r.Name == repositoryName) .Where(r => r.Owner.UserName == userName && r.Name == repositoryName)
.SelectMany(r => r.Commits) .SelectMany(r => r.Commits)
.Include(r => r.Author) .Include(r => r.Author).Include(repositoryCommit => repositoryCommit.MainDepot)
.OrderByDescending(r => r.CommittedOn) .OrderByDescending(r => r.CommittedOn)
.ToArrayAsync(); .AsAsyncEnumerable();
return Ok(new List<RepositoryCommitResponse> r = new();
{ await foreach (var cm in commit)
Commits = commit r.Add(new RepositoryCommitResponse(
}); cm.Id, cm.Author.UserName!, cm.CommittedOn, cm.Message, cm.MainDepot.DepotId));
return Ok(new ListingResponse<RepositoryCommitResponse>(r.ToArray()));
} }
[HttpGet("list_locked_files")] [HttpGet("list_locked_files")]
public async Task<IActionResult> ListLocksAsync(string userName, string repositoryName) public async Task<ActionResult<ListingResponse<LockFileInfo>>> ListLocksAsync(string userName, string repositoryName)
{ {
var user = (await userManager.GetUserAsync(HttpContext.User))!; var user = (await userManager.GetUserAsync(HttpContext.User))!;
var grantIssue = await ValidateRepositoryAsync(userName, repositoryName, user, RepositoryRole.Developer); var grantIssue = await ValidateRepositoryAsync(userName, repositoryName, user, RepositoryRole.Developer);
if (grantIssue is not Repository) return (IActionResult) grantIssue; if (grantIssue is not Repository) return (ActionResult) grantIssue;
var lockers = await dbContext.Repositories var lockers = await dbContext.Repositories
.Where(r => r.Owner.UserName == userName && r.Name == repositoryName) .Where(r => r.Owner.UserName == userName && r.Name == repositoryName)
.SelectMany(r => r.Locked) .SelectMany(r => r.Locked)
.Select(l => new { Path = l.Path, Owner = l.LockDownUser.UserName}) .Select(l => new LockFileInfo(l.Path, l.LockDownUser.UserName))
.ToArrayAsync(); .ToArrayAsync();
return Ok(new return Ok(new ListingResponse<LockFileInfo>(lockers));
{
Lockers = lockers
});
} }
[HttpGet("peek_commit")] [HttpGet("peek_commit")]
public async Task<IActionResult> PeekCommitAsync(string userName, string repositoryName) public async Task<ActionResult<PeekResponse<Guid>>> PeekCommitAsync(string userName, string repositoryName)
{ {
var user = (await userManager.GetUserAsync(HttpContext.User))!; var user = (await userManager.GetUserAsync(HttpContext.User))!;
var grantIssue = await ValidateRepositoryAsync(userName, repositoryName, user, RepositoryRole.Guest); var grantIssue = await ValidateRepositoryAsync(userName, repositoryName, user, RepositoryRole.Guest);
if (grantIssue is not Repository rp) return (IActionResult) grantIssue; if (grantIssue is not Repository rp) return (ActionResult) grantIssue;
var commit = await dbContext.Repositories var commit = await dbContext.Repositories
.Where(r => r.Owner.UserName == userName && r.Name == repositoryName) .Where(r => r.Owner.UserName == userName && r.Name == repositoryName)
@ -312,10 +308,7 @@ public class RepositoryInnieController(
.OrderByDescending(r => r.CommittedOn) .OrderByDescending(r => r.CommittedOn)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
return Ok(new return Ok(new PeekResponse<Guid>(commit?.Id ?? Guid.Empty));
{
Commit = commit?.Id.ToString() ?? string.Empty
});
} }
@ -394,6 +387,7 @@ public class RepositoryInnieController(
} }
[HttpPost("create_commit")] [HttpPost("create_commit")]
[ProducesResponseType<CommitSuccessResponse>(200)]
public async Task<IActionResult> CommitAsync(string userName, string repositoryName, [FromForm] FormCommitRequest req) public async Task<IActionResult> CommitAsync(string userName, string repositoryName, [FromForm] FormCommitRequest req)
{ {
var user = (await userManager.GetUserAsync(HttpContext.User))!; var user = (await userManager.GetUserAsync(HttpContext.User))!;
@ -538,11 +532,7 @@ public class RepositoryInnieController(
throw; throw;
} }
return Ok(new return Ok(new CommitSuccessResponse(commit.CommittedOn, commit.Id));
{
CreatedAt = commit.CommittedOn,
CommitId = commit.Id
});
} }
private static async ValueTask StencilWorkspaceSnapshotAsync(Stream depotStream, HashSet<WorkspaceFile> unresolved) private static async ValueTask StencilWorkspaceSnapshotAsync(Stream depotStream, HashSet<WorkspaceFile> unresolved)

View File

@ -14,34 +14,30 @@ namespace Flawless.Server.Controllers;
[ApiController, Authorize, Route("api")] [ApiController, Authorize, Route("api")]
public class RepositoryOutieController(AppDbContext dbContext, UserManager<AppUser> userManager) : ControllerBase public class RepositoryOutieController(AppDbContext dbContext, UserManager<AppUser> userManager) : ControllerBase
{ {
[HttpGet("repo_list")] [HttpGet("repo_list")]
public async Task<IActionResult> ListAllAvailableRepositoriesAsync([FromQuery] QueryPagesRequest r) public async Task<ActionResult<ListingResponse<RepositoryInfoResponse>>> ListAllAvailableRepositoriesAsync()
{ {
var u = (await userManager.GetUserAsync(HttpContext.User))!; var u = (await userManager.GetUserAsync(HttpContext.User))!;
var query = await dbContext.Repositories var query = dbContext.Repositories
.Include(repository => repository.Owner) .Include(repository => repository.Owner)
.Include(repository => repository.Commits) .Include(repository => repository.Commits)
.Include(repository => repository.Members) .Include(repository => repository.Members)
.ThenInclude(repositoryMember => repositoryMember.User) .ThenInclude(repositoryMember => repositoryMember.User)
.Where(rp => rp.Members.Any(m => m.User == u)) .Where(rp => rp.Members.Any(m => m.User == u))
.Skip(r.Offset) .Select(rp => new RepositoryInfoResponse
.Take(r.Length) {
.ToArrayAsync(); RepositoryName = rp.Name,
OwnerUsername = rp.Owner.UserName!,
LatestCommitId = rp.Commits.OrderByDescending(cm => cm.CommittedOn)
.Select(cm => cm.Id).FirstOrDefault(),
Description = rp.Description,
IsArchived = rp.IsArchived,
Role = rp.Members.First(m => m.User == u).Role
})
.ToArray();
return Ok(new PagedResponse<RepositoryInfoResponse> return Ok(new ListingResponse<RepositoryInfoResponse>(query));
{
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("repo_create")] [HttpPost("repo_create")]

View File

@ -94,22 +94,15 @@ public class UserController(
} }
[HttpGet("query_info")] [HttpGet("query_info")]
public async Task<ActionResult<PagedResponse<UserInfoResponse>>> GetUserInfoAsync(QueryPagesRequest r, [FromQuery] string keyword) public async Task<ActionResult<ListingResponse<UserInfoResponse>>> QueryUserInfoAsync([FromQuery] string keyword)
{ {
var payload = await userManager.Users var payload = await userManager.Users
.Where(u => u.UserName!.Contains(keyword) || (u.NickName != null && u.NickName.Contains(keyword))) .Where(u => u.UserName!.Contains(keyword) || (u.NickName != null && u.NickName.Contains(keyword)))
.Skip(r.Offset)
.Take(r.Length)
.Select(u => GetUserInfoInternal(u, null)) .Select(u => GetUserInfoInternal(u, null))
.ToArrayAsync(); .ToArrayAsync();
// Return self as default // Return self as default
return Ok(new PagedResponse<UserInfoResponse> return Ok(new ListingResponse<UserInfoResponse>(payload));
{
Offset = r.Offset,
Length = r.Length,
Data = payload
});
} }
[HttpGet("delete")] [HttpGet("delete")]

View File

@ -37,4 +37,8 @@
<Compile Remove="Migrations\20250322194407_InitialCreate.Designer.cs" /> <Compile Remove="Migrations\20250322194407_InitialCreate.Designer.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Exceptions\" />
</ItemGroup>
</Project> </Project>

View File

@ -6,7 +6,10 @@ public class ExceptionTransformMiddleware(RequestDelegate next)
{ {
public async Task InvokeAsync(HttpContext context) public async Task InvokeAsync(HttpContext context)
{ {
try { await next(context); } try
{
await next(context);
}
catch (Exception e) catch (Exception e)
{ {
context.Response.StatusCode = 500; context.Response.StatusCode = 500;