Commit MWS backend source tree

The ASP.NET Core solution (mws.api/mws.application/mws.domain/
mws.infrastructure) existed only as untracked working files. Adding
it to version control, plus a .gitignore for build output and local
tooling directories, so the CQRS/mediator refactor plan has a real
git history to branch and diff against.
This commit is contained in:
2026-08-13 23:10:22 +07:00
parent 96944ad2c3
commit f72aaa2329
94 changed files with 7758 additions and 0 deletions
+43
View File
@@ -0,0 +1,43 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Mws.Application.Accounts;
namespace Mws.Api.Controllers;
[ApiController]
[Route("api/accounts")]
[Authorize]
public class AccountsController(IAccountService accountService) : ControllerBase
{
[HttpGet]
public async Task<ActionResult<List<AccountDto>>> GetAll([FromQuery] string? q, CancellationToken ct)
{
return Ok(await accountService.GetAccountsAsync(User.GetUserId(), q, ct));
}
[HttpPost]
public async Task<ActionResult<AccountDto>> Create([FromBody] CreateAccountRequest request, CancellationToken ct)
{
return Ok(await accountService.CreateAccountAsync(User.GetUserId(), request, ct));
}
[HttpPut("{id:guid}")]
public async Task<ActionResult<AccountDto>> Update(Guid id, [FromBody] UpdateAccountRequest request, CancellationToken ct)
{
return Ok(await accountService.UpdateAccountAsync(User.GetUserId(), id, request, ct));
}
[HttpDelete("{id:guid}")]
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
{
await accountService.DeleteAccountAsync(User.GetUserId(), id, ct);
return NoContent();
}
[HttpPost("{id:guid}/reset-password")]
public async Task<IActionResult> ResetPassword(Guid id, [FromBody] ResetPasswordRequest request, CancellationToken ct)
{
await accountService.ResetPasswordAsync(User.GetUserId(), id, request, ct);
return NoContent();
}
}
+18
View File
@@ -0,0 +1,18 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Mws.Application.Auth;
namespace Mws.Api.Controllers;
[ApiController]
[Route("api/auth")]
[AllowAnonymous]
public class AuthController(IAuthService authService) : ControllerBase
{
[HttpPost("login")]
public async Task<ActionResult<LoginResponse>> Login([FromBody] LoginRequest request, CancellationToken ct)
{
var response = await authService.LoginAsync(request, ct);
return Ok(response);
}
}
@@ -0,0 +1,56 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Mws.Application.Documents;
namespace Mws.Api.Controllers;
[ApiController]
[Route("api")]
[Authorize]
public class DocumentsController(IDocumentService documentService) : ControllerBase
{
[HttpGet("documents/search")]
public async Task<ActionResult<List<DocumentNodeDto>>> Search([FromQuery] string? q, CancellationToken ct)
{
return Ok(await documentService.SearchAsync(User.GetUserId(), q ?? string.Empty, ct));
}
[HttpGet("projects/{projectId:guid}/documents")]
public async Task<ActionResult<List<DocumentNodeDto>>> GetTree(Guid projectId, CancellationToken ct)
{
return Ok(await documentService.GetTreeAsync(User.GetUserId(), projectId, ct));
}
[HttpPost("projects/{projectId:guid}/documents")]
public async Task<ActionResult<DocumentDto>> Create(Guid projectId, [FromBody] CreateDocumentRequest request, CancellationToken ct)
{
var result = await documentService.CreateAsync(User.GetUserId(), projectId, request, ct);
return CreatedAtAction(nameof(Get), new { id = result.Id }, result);
}
[HttpGet("documents/{id:guid}")]
public async Task<ActionResult<DocumentDto>> Get(Guid id, CancellationToken ct)
{
return Ok(await documentService.GetAsync(User.GetUserId(), id, ct));
}
[HttpPut("documents/{id:guid}")]
public async Task<ActionResult<DocumentDto>> Update(Guid id, [FromBody] UpdateDocumentRequest request, CancellationToken ct)
{
return Ok(await documentService.UpdateAsync(User.GetUserId(), id, request, ct));
}
[HttpPut("documents/{id:guid}/move")]
public async Task<ActionResult<DocumentDto>> Move(Guid id, [FromBody] MoveDocumentRequest request, CancellationToken ct)
{
await documentService.MoveAsync(User.GetUserId(), id, request.NewParentId, ct);
return Ok(await documentService.GetAsync(User.GetUserId(), id, ct));
}
[HttpDelete("documents/{id:guid}")]
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
{
await documentService.DeleteAsync(User.GetUserId(), id, ct);
return NoContent();
}
}
+17
View File
@@ -0,0 +1,17 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Mws.Application.Permissions;
namespace Mws.Api.Controllers;
[ApiController]
[Route("api/menu")]
[Authorize]
public class MenuController(IPermissionService permissions) : ControllerBase
{
[HttpGet]
public async Task<ActionResult<List<MenuItemDto>>> GetMenu(CancellationToken ct)
{
return Ok(await permissions.GetMenuAsync(User.GetUserId(), ct));
}
}
@@ -0,0 +1,37 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Mws.Application.Projects;
namespace Mws.Api.Controllers;
[ApiController]
[Route("api/projects/{projectId:guid}/members")]
[Authorize]
public class ProjectMembersController(IProjectMemberService memberService) : ControllerBase
{
[HttpGet]
public async Task<ActionResult<List<ProjectMemberDto>>> GetAll(Guid projectId, CancellationToken ct)
{
return Ok(await memberService.GetMembersAsync(User.GetUserId(), projectId, ct));
}
[HttpPost]
public async Task<ActionResult<ProjectMemberDto>> Add(Guid projectId, [FromBody] AddMemberRequest request, CancellationToken ct)
{
return Ok(await memberService.AddMemberAsync(User.GetUserId(), projectId, request, ct));
}
[HttpPut("{userId:guid}/document-permissions")]
public async Task<ActionResult<ProjectMemberDto>> UpdateDocumentPermissions(
Guid projectId, Guid userId, [FromBody] UpdateMemberDocumentPermissionsRequest request, CancellationToken ct)
{
return Ok(await memberService.UpdateMemberDocumentPermissionsAsync(User.GetUserId(), projectId, userId, request, ct));
}
[HttpDelete("{userId:guid}")]
public async Task<IActionResult> Remove(Guid projectId, Guid userId, CancellationToken ct)
{
await memberService.RemoveMemberAsync(User.GetUserId(), projectId, userId, ct);
return NoContent();
}
}
+55
View File
@@ -0,0 +1,55 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Mws.Application.Projects;
namespace Mws.Api.Controllers;
[ApiController]
[Route("api/projects")]
[Authorize]
public class ProjectsController(IProjectService projectService) : ControllerBase
{
[HttpGet]
public async Task<ActionResult<List<ProjectDto>>> GetAll(CancellationToken ct)
{
return Ok(await projectService.GetProjectsAsync(User.GetUserId(), ct));
}
[HttpGet("search")]
public async Task<ActionResult<List<ProjectDto>>> Search([FromQuery] string? q, CancellationToken ct)
{
return Ok(await projectService.SearchAsync(User.GetUserId(), q ?? string.Empty, ct));
}
[HttpGet("{projectId:guid}")]
public async Task<ActionResult<ProjectDto>> Get(Guid projectId, CancellationToken ct)
{
return Ok(await projectService.GetProjectAsync(User.GetUserId(), projectId, ct));
}
[HttpGet("{projectId:guid}/overview")]
public async Task<ActionResult<ProjectOverviewDto>> Overview(Guid projectId, CancellationToken ct)
{
return Ok(await projectService.GetOverviewAsync(User.GetUserId(), projectId, ct));
}
[HttpPost]
public async Task<ActionResult<ProjectDto>> Create([FromBody] CreateProjectRequest request, CancellationToken ct)
{
var result = await projectService.CreateProjectAsync(User.GetUserId(), request, ct);
return CreatedAtAction(nameof(Get), new { projectId = result.Id }, result);
}
[HttpPut("{projectId:guid}")]
public async Task<ActionResult<ProjectDto>> Update(Guid projectId, [FromBody] UpdateProjectRequest request, CancellationToken ct)
{
return Ok(await projectService.UpdateProjectAsync(User.GetUserId(), projectId, request, ct));
}
[HttpDelete("{projectId:guid}")]
public async Task<IActionResult> Delete(Guid projectId, CancellationToken ct)
{
await projectService.ArchiveProjectAsync(User.GetUserId(), projectId, ct);
return NoContent();
}
}
+48
View File
@@ -0,0 +1,48 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Mws.Application.Permissions;
using Mws.Application.Roles;
namespace Mws.Api.Controllers;
[ApiController]
[Route("api/roles")]
[Authorize]
public class RolesController(IRoleService roleService, IPermissionService permissions) : ControllerBase
{
[HttpGet]
public async Task<ActionResult<List<RoleDto>>> GetAll(CancellationToken ct)
{
await permissions.EnsureAsync(User.GetUserId(), "roles", PermissionAction.View, ct);
return Ok(await roleService.GetRolesAsync(ct));
}
[HttpGet("{id:guid}")]
public async Task<ActionResult<RoleDto>> Get(Guid id, CancellationToken ct)
{
await permissions.EnsureAsync(User.GetUserId(), "roles", PermissionAction.View, ct);
return Ok(await roleService.GetRoleAsync(id, ct));
}
[HttpPost]
public async Task<ActionResult<RoleDto>> Create([FromBody] SaveRoleRequest request, CancellationToken ct)
{
await permissions.EnsureAsync(User.GetUserId(), "roles", PermissionAction.Create, ct);
return Ok(await roleService.CreateRoleAsync(request, ct));
}
[HttpPut("{id:guid}")]
public async Task<ActionResult<RoleDto>> Update(Guid id, [FromBody] SaveRoleRequest request, CancellationToken ct)
{
await permissions.EnsureAsync(User.GetUserId(), "roles", PermissionAction.Edit, ct);
return Ok(await roleService.UpdateRoleAsync(id, request, ct));
}
[HttpDelete("{id:guid}")]
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
{
await permissions.EnsureAsync(User.GetUserId(), "roles", PermissionAction.Delete, ct);
await roleService.DeleteRoleAsync(id, ct);
return NoContent();
}
}
+68
View File
@@ -0,0 +1,68 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Mws.Application.Common;
using Mws.Application.Tasks;
using TaskPriority = Mws.Domain.Tasks.TaskPriority;
using TaskStatus = Mws.Domain.Tasks.TaskStatus;
namespace Mws.Api.Controllers;
[ApiController]
[Route("api")]
[Authorize]
public class TasksController(ITaskService taskService) : ControllerBase
{
[HttpGet("tasks/search")]
public async Task<ActionResult<List<TaskDto>>> Search([FromQuery] string? q, CancellationToken ct)
{
return Ok(await taskService.SearchAsync(User.GetUserId(), q ?? string.Empty, ct));
}
[HttpGet("projects/{projectId:guid}/tasks")]
public async Task<ActionResult<List<TaskDto>>> GetAll(
Guid projectId,
[FromQuery] string? status,
[FromQuery] string? priority,
[FromQuery] Guid? assigneeId,
CancellationToken ct)
{
var statusValue = ParseOptional<TaskStatus>(status);
var priorityValue = ParseOptional<TaskPriority>(priority);
return Ok(await taskService.GetTasksAsync(User.GetUserId(), projectId, statusValue, priorityValue, assigneeId, ct));
}
[HttpPost("projects/{projectId:guid}/tasks")]
public async Task<ActionResult<TaskDto>> Create(Guid projectId, [FromBody] CreateTaskRequest request, CancellationToken ct)
{
var result = await taskService.CreateAsync(User.GetUserId(), projectId, request, ct);
return CreatedAtAction(nameof(Get), new { id = result.Id }, result);
}
[HttpGet("tasks/{id:guid}")]
public async Task<ActionResult<TaskDto>> Get(Guid id, CancellationToken ct)
{
return Ok(await taskService.GetAsync(User.GetUserId(), id, ct));
}
[HttpPut("tasks/{id:guid}")]
public async Task<ActionResult<TaskDto>> Update(Guid id, [FromBody] UpdateTaskRequest request, CancellationToken ct)
{
return Ok(await taskService.UpdateAsync(User.GetUserId(), id, request, ct));
}
[HttpDelete("tasks/{id:guid}")]
public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
{
await taskService.DeleteAsync(User.GetUserId(), id, ct);
return NoContent();
}
private static TEnum? ParseOptional<TEnum>(string? value) where TEnum : struct, Enum
{
if (string.IsNullOrWhiteSpace(value))
{
return null;
}
return Enum.TryParse<TEnum>(value, ignoreCase: true, out var result) ? result : throw new BadRequestException($"Invalid {typeof(TEnum).Name}: {value}");
}
}
+20
View File
@@ -0,0 +1,20 @@
using AutoMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Mws.Application.Common;
using Mws.Application.Auth;
namespace Mws.Api.Controllers;
[ApiController]
[Route("api/users")]
[Authorize]
public class UsersController(IUnitOfWork uow, IMapper mapper) : ControllerBase
{
[HttpGet]
public async Task<ActionResult<List<UserDto>>> GetAll([FromQuery] string? q, CancellationToken ct)
{
var users = await uow.Users.SearchWithRoleAsync(q, 50, ct);
return Ok(mapper.Map<List<UserDto>>(users));
}
}