feat: Add basic supports on Repository support.
This commit is contained in:
parent
271165fb88
commit
111eca1b3c
@ -1,3 +0,0 @@
|
||||
namespace Flawless.Communication.Request;
|
||||
|
||||
public record LocateUserRequest(string? UserId, string? Username);
|
||||
@ -7,4 +7,11 @@ public class QueryPagesRequest<T>
|
||||
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; }
|
||||
}
|
||||
@ -6,7 +6,7 @@ public record PagedResponse<T>
|
||||
|
||||
public required int Length { get; init; }
|
||||
|
||||
public uint? Total { get; init; }
|
||||
public int? Total { get; init; }
|
||||
|
||||
public IEnumerable<T>? Data { get; init; }
|
||||
}
|
||||
|
||||
18
Flawless.Communication/Response/RepositoryInfoResponse.cs
Normal file
18
Flawless.Communication/Response/RepositoryInfoResponse.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using Flawless.Communication.Shared;
|
||||
|
||||
namespace Flawless.Communication.Response;
|
||||
|
||||
public record RepositoryInfoResponse
|
||||
{
|
||||
public required string RepositoryName { get; set; }
|
||||
|
||||
public required string OwnerUsername { get; set; }
|
||||
|
||||
public required Guid LatestCommitId { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
public required bool IsArchived { get; set; }
|
||||
|
||||
public required RepositoryRole Role { get; set; }
|
||||
}
|
||||
8
Flawless.Communication/Shared/RepoUserRole.cs
Normal file
8
Flawless.Communication/Shared/RepoUserRole.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Flawless.Communication.Shared;
|
||||
|
||||
public record RepoUserRole
|
||||
{
|
||||
public required string Username { get; set; }
|
||||
|
||||
public RepositoryRole? Role { get; set; }
|
||||
}
|
||||
9
Flawless.Communication/Shared/RepositoryRole.cs
Normal file
9
Flawless.Communication/Shared/RepositoryRole.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace Flawless.Communication.Shared;
|
||||
|
||||
public enum RepositoryRole
|
||||
{
|
||||
Guest = 0,
|
||||
Reporter = 1,
|
||||
Developer = 2,
|
||||
Owner = 3,
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
namespace Flawless.Core.Interface;
|
||||
|
||||
/// <summary>
|
||||
/// Standardized interface to describe a place to store depots and how they connected with each other.
|
||||
/// </summary>
|
||||
public interface IReadonlyRepository
|
||||
{
|
||||
public bool IsReadonly { get; }
|
||||
|
||||
public IEnumerable<IRepositoryCommit> GetCommits();
|
||||
|
||||
public IRepositoryCommit? GetCommitById(uint commitId);
|
||||
|
||||
public IAsyncEnumerable<IRepositoryCommit> GetCommitsAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
public Task<IRepositoryCommit?> GetCommitByIdAsync(uint commitId, CancellationToken cancellationToken = default);
|
||||
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
using Flawless.Core.Modal;
|
||||
|
||||
namespace Flawless.Core.Interface;
|
||||
|
||||
public interface IRepositoryCommit
|
||||
{
|
||||
public IReadonlyRepository Repository { get; }
|
||||
|
||||
public UInt64 CommitId { get; }
|
||||
|
||||
public Author Author { get; }
|
||||
|
||||
public DateTime CommitTime { get; }
|
||||
|
||||
public string Message { get; }
|
||||
|
||||
public ulong ManifestId { get; }
|
||||
|
||||
public IRepositoryCommit? GetParentCommit();
|
||||
|
||||
public IRepositoryCommit? GetChildCommit();
|
||||
}
|
||||
@ -4,4 +4,4 @@
|
||||
/// An author setup to indicate who create a depot or identify a depot author when uploading it.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public record struct Author(string Name, string Email);
|
||||
public record struct Author(string Name, Guid Email);
|
||||
@ -1,4 +1,4 @@
|
||||
namespace Flawless.Core.Modal;
|
||||
|
||||
[Serializable]
|
||||
public record struct CommitManifest(ulong ManifestId, DepotLabel Depot, string[] FilePaths);
|
||||
public record struct CommitManifest(Guid ManifestId, DepotLabel Depot, string[] FilePaths);
|
||||
@ -3,4 +3,4 @@
|
||||
namespace Flawless.Core.Modal;
|
||||
|
||||
[Serializable]
|
||||
public record struct DepotLabel(HashId Id, HashId[] Baselines);
|
||||
public record struct DepotLabel(HashId Id, HashId[] Dependency);
|
||||
@ -11,11 +11,10 @@ namespace Flawless.Server.Controllers;
|
||||
public class AdminUserController(
|
||||
UserManager<AppUser> userManager) : ControllerBase
|
||||
{
|
||||
[HttpPost("user/delete")]
|
||||
public async Task<IActionResult> DeleteUserAsync(LocateUserRequest r)
|
||||
[HttpPost("user/delete/{username}")]
|
||||
public async Task<IActionResult> DeleteUserAsync(string username)
|
||||
{
|
||||
if (r.UserId == null) return BadRequest(new FailedResponse("User id is not set!"));
|
||||
var user = await userManager.FindByIdAsync(r.UserId);
|
||||
var user = await userManager.FindByNameAsync(username);
|
||||
|
||||
if (user == null) return BadRequest(new FailedResponse("User does not exist!"));
|
||||
var result = await userManager.DeleteAsync(user);
|
||||
@ -24,11 +23,10 @@ public class AdminUserController(
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("user/enable")]
|
||||
public async Task<IActionResult> EnableUserAsync(LocateUserRequest r)
|
||||
[HttpPost("user/enable/{username}")]
|
||||
public async Task<IActionResult> EnableUserAsync(string username)
|
||||
{
|
||||
if (r.UserId == null) return BadRequest(new FailedResponse("User id is not set!"));
|
||||
var user = await userManager.FindByIdAsync(r.UserId);
|
||||
var user = await userManager.FindByNameAsync(username);
|
||||
|
||||
if (user == null) return BadRequest(new FailedResponse("User does not exist!"));
|
||||
var result = await userManager.SetLockoutEnabledAsync(user, false);
|
||||
@ -37,11 +35,10 @@ public class AdminUserController(
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("user/disable")]
|
||||
public async Task<IActionResult> DisableUserAsync(LocateUserRequest r)
|
||||
[HttpPost("user/disable/{username}")]
|
||||
public async Task<IActionResult> DisableUserAsync(string username)
|
||||
{
|
||||
if (r.UserId == null) return BadRequest(new FailedResponse("User id is not set!"));
|
||||
var user = await userManager.FindByIdAsync(r.UserId);
|
||||
var user = await userManager.FindByNameAsync(username);
|
||||
|
||||
if (user == null) return BadRequest(new FailedResponse("User does not exist!"));
|
||||
var result = await userManager.SetLockoutEnabledAsync(user, true);
|
||||
|
||||
10
Flawless.Server/Controllers/RepositoryControl.cs
Normal file
10
Flawless.Server/Controllers/RepositoryControl.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Flawless.Server.Controllers;
|
||||
|
||||
[ApiController, Authorize, Route("api/repo/{userName}/{repositoryName}")]
|
||||
public class RepositoryControl : ControllerBase
|
||||
{
|
||||
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Flawless.Server.Controllers;
|
||||
|
||||
[ApiController, Route("api/repository")]
|
||||
public class RepositoryController : ControllerBase
|
||||
{
|
||||
|
||||
}
|
||||
232
Flawless.Server/Controllers/RepositoryManageController.cs
Normal file
232
Flawless.Server/Controllers/RepositoryManageController.cs
Normal file
@ -0,0 +1,232 @@
|
||||
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<AppUser> userManager) : ControllerBase
|
||||
{
|
||||
[HttpGet("list")]
|
||||
public async Task<IActionResult> 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<RepositoryInfoResponse>
|
||||
{
|
||||
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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<IActionResult> 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<RepoUserRole>
|
||||
{
|
||||
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<IActionResult> 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<IActionResult> 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();
|
||||
}
|
||||
}
|
||||
@ -81,38 +81,31 @@ public class UserController(
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpGet("get/info")]
|
||||
public async Task<ActionResult<UserInfoResponse>> GetUserInfoAsync(LocateUserRequest r)
|
||||
[HttpGet("get")]
|
||||
public async Task<ActionResult<UserInfoResponse>> GetUserInfoAsync()
|
||||
{
|
||||
var self = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
|
||||
if (r.UserId != null)
|
||||
{
|
||||
var u = await userManager.FindByIdAsync(r.UserId);
|
||||
if (u == null) return BadRequest(new FailedResponse("User is not existed!"));
|
||||
|
||||
return Ok(GetUserInfoInternal(u, self));
|
||||
}
|
||||
|
||||
if (r.Username != null)
|
||||
{
|
||||
var u = await userManager.FindByNameAsync(r.Username);
|
||||
if (u == null) return BadRequest(new FailedResponse("User is not existed!"));
|
||||
|
||||
return Ok(GetUserInfoInternal(u, self));
|
||||
}
|
||||
|
||||
// Return self as default
|
||||
return Ok(GetUserInfoInternal(self, self));
|
||||
}
|
||||
|
||||
[HttpGet("query/info")]
|
||||
public async Task<ActionResult<PagedResponse<UserInfoResponse>>> GetUserInfoAsync(QueryPagesRequest<LocateUserRequest> r)
|
||||
[HttpGet("get/{username}")]
|
||||
public async Task<ActionResult<UserInfoResponse>> GetUserInfoAsync(string username)
|
||||
{
|
||||
var self = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
|
||||
var u = await userManager.FindByNameAsync(username);
|
||||
if (u == null) return BadRequest(new FailedResponse("User is not existed!"));
|
||||
|
||||
return Ok(GetUserInfoInternal(u, self));
|
||||
}
|
||||
|
||||
[HttpGet("query/{keyword}")]
|
||||
public async Task<ActionResult<PagedResponse<UserInfoResponse>>> GetUserInfoAsync(QueryPagesRequest r, string keyword)
|
||||
{
|
||||
var queryNamePrefix = r.Parameter?.Username ?? String.Empty;
|
||||
var queryId = r.Parameter == null ? Guid.Empty : Guid.Parse(r.Parameter.UserId!);
|
||||
var payload = await userManager.Users
|
||||
.Where(u => u.UserName!.StartsWith(queryNamePrefix) || u.Id == queryId)
|
||||
.Where(u => u.UserName!.Contains(keyword) || (u.NickName != null && u.NickName.Contains(keyword)))
|
||||
.Skip(r.Offset)
|
||||
.Take(r.Length)
|
||||
.Select(u => GetUserInfoInternal(u, null))
|
||||
@ -141,12 +134,15 @@ public class UserController(
|
||||
var authorized = queryUser.Id == currentUser?.Id;
|
||||
return new UserInfoResponse
|
||||
{
|
||||
Authorized = authorized,
|
||||
Username = queryUser.UserName,
|
||||
CreatedAt = queryUser.CreatedOn,
|
||||
Bio = queryUser.Bio,
|
||||
Gender = queryUser.Gender,
|
||||
NickName = queryUser.NickName,
|
||||
PublicEmail = authorized ? queryUser.PublicEmail : null,
|
||||
Email = queryUser.PublicEmail || authorized ? queryUser.Email : null,
|
||||
Phone = authorized ? queryUser.PhoneNumber : null,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1,19 +1,46 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Flawless.Server.Models;
|
||||
using Flawless.Server.Services;
|
||||
using Flawless.Server.Utility;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Flawless.Server.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("ws/transfer")]
|
||||
public class WebSocketTransferController : ControllerBase
|
||||
[ApiController, Authorize, Route("ws/transfer")]
|
||||
public class WebSocketTransferController(
|
||||
PathTransformer transformer,
|
||||
RepoTransmissionContext transmission,
|
||||
UserManager<AppUser> userManager) : ControllerBase
|
||||
{
|
||||
[Route("download")]
|
||||
public async Task DownloadDepotAsync([FromHeader] string resource, [FromHeader] string? validate)
|
||||
{
|
||||
}
|
||||
|
||||
[Route("upload")]
|
||||
public async Task UploadDepotAsync([FromHeader] string resource, [FromHeader] string? validate)
|
||||
[Route("download/{taskId}")]
|
||||
public async Task DownloadDepotAsync(string taskId)
|
||||
{
|
||||
var (u, task) = await GetTaskAsync(transmission.DownloadTask, taskId);
|
||||
|
||||
}
|
||||
|
||||
[Route("upload/{taskId}")]
|
||||
public async Task UploadDepotAsync(string taskId)
|
||||
{
|
||||
var (u, task) = await GetTaskAsync(transmission.DownloadTask, taskId);
|
||||
|
||||
}
|
||||
|
||||
private async ValueTask<(AppUser user, RepoDepotTransmissionTask task)> GetTaskAsync
|
||||
(IDictionary<Guid, RepoDepotTransmissionTask> taskList, string taskId)
|
||||
{
|
||||
if (!Guid.TryParse(taskId, out var id))
|
||||
throw new ArgumentException("Not a valid task id!", nameof(taskId));
|
||||
|
||||
if (!taskList.TryGetValue(id, out var task))
|
||||
throw new ArgumentException("Task not found.", nameof(taskId));
|
||||
|
||||
var u = (await userManager.GetUserAsync(HttpContext.User))!;
|
||||
if (task.UserId != u.Id) throw new ArgumentException("Not being authorized!", nameof(taskId));
|
||||
if (DateTime.UtcNow > task.ExpiresOn)
|
||||
throw new ArgumentException("Task is expired!", nameof(taskId));
|
||||
|
||||
return (u, task);
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,7 @@ namespace Flawless.Server.Models;
|
||||
|
||||
public class AppUser : IdentityUser<Guid>
|
||||
{
|
||||
[Required]
|
||||
public DateTime CreatedOn { get; set; }
|
||||
|
||||
public UserSex Gender { get; set; }
|
||||
@ -16,8 +17,9 @@ public class AppUser : IdentityUser<Guid>
|
||||
|
||||
[MaxLength(200)]
|
||||
public string? Bio { get; set; }
|
||||
|
||||
public bool PublicEmail { get; set; }
|
||||
|
||||
[Required]
|
||||
public bool PublicEmail { get; set; } = true;
|
||||
|
||||
|
||||
public void RenewSecurityStamp()
|
||||
|
||||
@ -5,9 +5,12 @@ namespace Flawless.Server.Models;
|
||||
public class AppUserRefreshKey
|
||||
{
|
||||
[Key]
|
||||
[Required]
|
||||
public required string RefreshToken { get; set; }
|
||||
|
||||
public Guid UserId { get; set; }
|
||||
[Required]
|
||||
public required Guid UserId { get; set; }
|
||||
|
||||
public DateTime ExpireIn { get; set; }
|
||||
[Required]
|
||||
public required DateTime ExpireIn { get; set; }
|
||||
}
|
||||
12
Flawless.Server/Models/RepoDepotTransmissionTask.cs
Normal file
12
Flawless.Server/Models/RepoDepotTransmissionTask.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace Flawless.Server.Models;
|
||||
|
||||
public class RepoDepotTransmissionTask
|
||||
{
|
||||
public required Guid UserId { get; set; }
|
||||
|
||||
public required Guid RepositoryId { get; set; }
|
||||
|
||||
public required Guid DepotId { get; set; }
|
||||
|
||||
public required DateTime ExpiresOn { get; set; }
|
||||
}
|
||||
25
Flawless.Server/Models/Repository.cs
Normal file
25
Flawless.Server/Models/Repository.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Flawless.Server.Models;
|
||||
|
||||
public class Repository
|
||||
{
|
||||
[Key]
|
||||
[Required]
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
|
||||
[Required]
|
||||
public required AppUser Owner { get; set; }
|
||||
|
||||
[Required]
|
||||
public required string Name { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
[Required]
|
||||
public bool IsArchived { get; set; } = false;
|
||||
|
||||
public List<RepositoryMember> Members { get; set; } = new();
|
||||
|
||||
public List<RepositoryCommit> Commits { get; set; } = new();
|
||||
}
|
||||
20
Flawless.Server/Models/RepositoryCommit.cs
Normal file
20
Flawless.Server/Models/RepositoryCommit.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Flawless.Server.Models;
|
||||
|
||||
public class RepositoryCommit
|
||||
{
|
||||
[Required]
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
|
||||
[Required]
|
||||
public required DateTime CommittedOn { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Message { get; set; } = String.Empty;
|
||||
|
||||
[Required]
|
||||
public required RepositoryDepot MainDepot { get; set; }
|
||||
|
||||
public RepositoryCommit? Parent { get; set; }
|
||||
}
|
||||
13
Flawless.Server/Models/RepositoryDepot.cs
Normal file
13
Flawless.Server/Models/RepositoryDepot.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Flawless.Server.Models;
|
||||
|
||||
public class RepositoryDepot
|
||||
{
|
||||
[Key]
|
||||
[Required]
|
||||
public Guid DepotId { get; set; }
|
||||
|
||||
[Required]
|
||||
public List<RepositoryDepot> Dependencies { get; set; } = new();
|
||||
}
|
||||
17
Flawless.Server/Models/RepositoryMember.cs
Normal file
17
Flawless.Server/Models/RepositoryMember.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Flawless.Communication.Shared;
|
||||
|
||||
namespace Flawless.Server.Models;
|
||||
|
||||
public class RepositoryMember
|
||||
{
|
||||
[Key]
|
||||
[Required]
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
|
||||
[Required]
|
||||
public required AppUser User { get; set; }
|
||||
|
||||
[Required]
|
||||
public RepositoryRole Role { get; set; } = RepositoryRole.Guest;
|
||||
}
|
||||
@ -5,6 +5,7 @@ using Flawless.Communication.Response;
|
||||
using Flawless.Server.Middlewares;
|
||||
using Flawless.Server.Models;
|
||||
using Flawless.Server.Services;
|
||||
using Flawless.Server.Utility;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
@ -33,6 +34,8 @@ public static class Program
|
||||
private static void ConfigAppService(WebApplicationBuilder builder)
|
||||
{
|
||||
// Api related
|
||||
builder.Services.AddSingleton<PathTransformer>();
|
||||
builder.Services.AddSingleton<RepoTransmissionContext>();
|
||||
builder.Services.AddOpenApi();
|
||||
builder.Services.AddControllers()
|
||||
.AddJsonOptions(opt =>
|
||||
@ -50,7 +53,6 @@ public static class Program
|
||||
private static void ConfigDbContext(WebApplicationBuilder builder)
|
||||
{
|
||||
// Data connection related.
|
||||
builder.Services.AddDbContextFactory<RepositoryContext, RepositoryContextFactory>();
|
||||
builder.Services.AddDbContext<AppDbContext>(opt =>
|
||||
{
|
||||
opt.UseNpgsql(builder.Configuration.GetConnectionString("CoreDb"));
|
||||
|
||||
@ -10,4 +10,5 @@ public class AppDbContext(DbContextOptions<AppDbContext> options)
|
||||
{
|
||||
public DbSet<AppUserRefreshKey> RefreshTokens { get; set; }
|
||||
|
||||
public DbSet<Repository> Repositories { get; set; }
|
||||
}
|
||||
15
Flawless.Server/Services/RepoTransmissionContext.cs
Normal file
15
Flawless.Server/Services/RepoTransmissionContext.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Flawless.Server.Models;
|
||||
|
||||
namespace Flawless.Server.Services;
|
||||
|
||||
public class RepoTransmissionContext
|
||||
{
|
||||
private readonly ConcurrentDictionary<Guid, RepoDepotTransmissionTask> _uploadTask = new();
|
||||
|
||||
private readonly ConcurrentDictionary<Guid, RepoDepotTransmissionTask> _downloadTask = new();
|
||||
|
||||
public IDictionary<Guid, RepoDepotTransmissionTask> UploadTask => _uploadTask;
|
||||
|
||||
public IDictionary<Guid, RepoDepotTransmissionTask> DownloadTask => _downloadTask;
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Flawless.Server.Services;
|
||||
|
||||
public class RepositoryContext : DbContext
|
||||
{
|
||||
private readonly string RepositoryPath;
|
||||
|
||||
public RepositoryContext(IConfiguration config, DbContextOptions<RepositoryContext> options) : base(options)
|
||||
{
|
||||
RepositoryPath = Path.Combine(config["LocalStoragePath"] ?? "./Data", "Repository");
|
||||
|
||||
if (!Directory.Exists(RepositoryPath))
|
||||
Directory.CreateDirectory(RepositoryPath);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
|
||||
namespace Flawless.Server.Services;
|
||||
|
||||
public class RepositoryContextFactory : DbContextFactory<RepositoryContext>
|
||||
{
|
||||
public RepositoryContextFactory(IServiceProvider serviceProvider, DbContextOptions<RepositoryContext> options, IDbContextFactorySource<RepositoryContext> factorySource)
|
||||
: base(serviceProvider, options, factorySource)
|
||||
{
|
||||
}
|
||||
}
|
||||
34
Flawless.Server/Utility/PathTransformer.cs
Normal file
34
Flawless.Server/Utility/PathTransformer.cs
Normal file
@ -0,0 +1,34 @@
|
||||
namespace Flawless.Server.Utility;
|
||||
|
||||
public class PathTransformer(IConfiguration config)
|
||||
{
|
||||
|
||||
private readonly string _rootPath = config.GetValue<string>("LocalStoragePath") ?? "./Storage";
|
||||
|
||||
public string GetLocalStoragePath()
|
||||
{
|
||||
return _rootPath;
|
||||
}
|
||||
|
||||
public string GetRepositoryRootPath(Guid repositoryId)
|
||||
{
|
||||
if (repositoryId == Guid.Empty) throw new ArgumentException(nameof(repositoryId));
|
||||
return Path.Combine(_rootPath, repositoryId.ToString());
|
||||
}
|
||||
|
||||
public string GetCommitManifest(Guid repositoryId, Guid commitId)
|
||||
{
|
||||
if (repositoryId == Guid.Empty) throw new ArgumentException(nameof(repositoryId));
|
||||
if (commitId == Guid.Empty) throw new ArgumentException(nameof(commitId));
|
||||
|
||||
return Path.Combine(_rootPath, repositoryId.ToString(), commitId.ToString());
|
||||
}
|
||||
|
||||
public string GetDepotManifest(Guid repositoryId, Guid depotId)
|
||||
{
|
||||
if (repositoryId == Guid.Empty) throw new ArgumentException(nameof(repositoryId));
|
||||
if (depotId == Guid.Empty) throw new ArgumentException(nameof(depotId));
|
||||
|
||||
return Path.Combine(_rootPath, repositoryId.ToString(), depotId.ToString());
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user