Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: .NET CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'

- name: Restore
run: dotnet restore

- name: Build
run: dotnet build --no-restore --configuration Release

- name: Test
run: dotnet test --no-build --configuration Release --verbosity normal
68 changes: 68 additions & 0 deletions Library.API/Controllers/AuthController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using Library.Application.Dtos;
using Library.Application.Interfaces;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Library.API.Controllers;

[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
private readonly IAuthService _authService;

public AuthController(IAuthService authService)
{
_authService = authService;
}

[HttpPost("register")]
public async Task<IActionResult> Register([FromBody] RegisterRequest request)
{
try
{
var result = await _authService.RegisterAsync(request);
return Ok(result);
}
catch (InvalidOperationException ex)
{
return BadRequest(new { Message = ex.Message });
}
}

[HttpPost("login")]
public async Task<IActionResult> Login([FromBody] LoginRequest request)
{
try
{
var result = await _authService.LoginAsync(request);
return Ok(result);
}
catch (UnauthorizedAccessException ex)
{
return Unauthorized(new { Message = ex.Message });
}
}

[HttpPost("refresh")]
public async Task<IActionResult> Refresh([FromBody] RefreshRequest request)
{
try
{
var result = await _authService.RefreshAsync(request);
return Ok(result);
}
catch (UnauthorizedAccessException ex)
{
return Unauthorized(new { Message = ex.Message });
}
}

[HttpPost("revoke")]
[Authorize]
public async Task<IActionResult> Revoke([FromBody] RefreshRequest request)
{
await _authService.RevokeAsync(request.RefreshToken);
return NoContent();
}
}
106 changes: 59 additions & 47 deletions Library.API/Controllers/BooksController.cs
Original file line number Diff line number Diff line change
@@ -1,54 +1,66 @@
using Library.Application.Dtos;
using Library.Application.Interfaces;
using Library.Applictation.Dtos;
using Library.Applictation.Interfaces;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Library.API.Controllers
namespace Library.API.Controllers;

[ApiController]
[Route("api/[controller]")]
public class BooksController : ControllerBase
{
[Route("api/[controller]")]
[ApiController]
public class BooksController : ControllerBase
private readonly IBookService _bookService;

public BooksController(IBookService bookService)
{
_bookService = bookService;
}

[HttpGet]
public async Task<IActionResult> GetBooks([FromQuery] BookQueryParameters parameters)
{
var result = await _bookService.GetBooksAsync(parameters);
return Ok(result);
}

[HttpGet("{id}")]
public async Task<IActionResult> GetById(Guid id)
{
var book = await _bookService.GetBookByIdAsync(id);
if (book == null) return NotFound();
return Ok(book);
}

[HttpPost]
[Authorize(Roles = "Admin,Librarian")]
public async Task<IActionResult> Create([FromBody] CreateBookRequest request)
{
var created = await _bookService.CreateBookAsync(request);
return CreatedAtAction(nameof(GetById), new { id = created.Id }, created);
}

[HttpPut("{id}")]
[Authorize(Roles = "Admin,Librarian")]
public async Task<IActionResult> Update(Guid id, [FromBody] UpdateBookRequest request)
{
var updated = await _bookService.UpdateBookAsync(id, request);
return Ok(updated);
}

[HttpDelete("{id}")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> Delete(Guid id)
{
await _bookService.DeleteBookAsync(id);
return NoContent(); // 204 — успешно, тела нет
}

[HttpPost("{id}/restore")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> Restore(Guid id)
{
private readonly IBookService _bookService;

public BooksController(IBookService bookService)
{
_bookService = bookService;
}

[HttpGet]
public async Task<IActionResult> GetAll()
{
var books = await _bookService.GetAllBooksAsync();
return Ok(books);
}

[HttpGet("{id}")]
public async Task<IActionResult> GetById(Guid id)
{
var book = await _bookService.GetBookByIdAsync(id);

if (book == null)
{
return NotFound();
}

return Ok(book);
}

[HttpPost]
public async Task<IActionResult> Create([FromBody] CreateBookRequest request)
{
var created = await _bookService.CreateBookAsync(request);

return CreatedAtAction(nameof(GetById), new {id = created.Id}, created);
}

[HttpGet]
public async Task<IActionResult> GetBooks([FromQuery] BookQueryParameters parameters)
{
var result = await _bookService.GetBooksAsync(parameters);
return Ok(result);
}
await _bookService.RestoreBookAsync(id);
return NoContent();
}
}
}
70 changes: 70 additions & 0 deletions Library.API/Controllers/LoansController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using Library.Application.Dtos;
using Library.Application.Interfaces;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Library.API.Controllers;

[ApiController]
[Route("api/[controller]")]
[Authorize]
public class LoansController : ControllerBase
{
private readonly ILoanService _loanService;

public LoansController(ILoanService loanService)
{
_loanService = loanService;
}

[HttpGet]
[Authorize(Roles = "Admin,Librarian")]
public async Task<IActionResult> GetAll()
{
var loans = await _loanService.GetAllLoansAsync();
return Ok(loans);
}

[HttpGet("user/{userId}")]
public async Task<IActionResult> GetUserLoans(Guid userId)
{
var loans = await _loanService.GetUserLoansAsync(userId);
return Ok(loans);
}

[HttpPost("borrow")]
public async Task<IActionResult> Borrow([FromBody] BorrowRequest request)
{
try
{
var result = await _loanService.BorrowBookAsync(request);
return CreatedAtAction(nameof(GetUserLoans), new { userId = request.UserId }, result);
}
catch (InvalidOperationException ex)
{
return BadRequest(new { Message = ex.Message });
}
catch (KeyNotFoundException ex)
{
return NotFound(new { Message = ex.Message });
}
}

[HttpPost("return")]
public async Task<IActionResult> Return([FromBody] ReturnRequest request)
{
try
{
var result = await _loanService.ReturnBookAsync(request);
return Ok(result);
}
catch (InvalidOperationException ex)
{
return BadRequest(new { Message = ex.Message });
}
catch (KeyNotFoundException ex)
{
return NotFound(new { Message = ex.Message });
}
}
}
90 changes: 90 additions & 0 deletions Library.API/Controllers/UserController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using Library.Application.Dtos;
using Library.Applictation.Interfaces;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;

namespace Library.API.Controllers
{
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class UserController : ControllerBase
{
private readonly IUserService _userService;

public UserController(IUserService userService)
{
_userService = userService;
}

[HttpGet]
[Authorize(Roles = "Admin, Librarian")]
public async Task<IActionResult> GetAll()
{
return Ok(await _userService.GetAllUsersAsync());
}

[HttpGet("{id}")]
public async Task<IActionResult> GetById(Guid id)
{
var currentUserId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var isStaff = User.IsInRole("Admin") || User.IsInRole("Librarian");

if (!isStaff && currentUserId != id.ToString())
{
return Forbid();
}

return Ok(await _userService.GetUserByIdAsync(id));
}

[HttpPut("{id}")]
public async Task<IActionResult> Update(Guid id, [FromBody] UpdateUserRequest request)
{
var currentUserId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var isStaff = User.IsInRole("Admin") || User.IsInRole("Librarian");

if (!isStaff && currentUserId == id.ToString())
{
return Forbid();
}

return Ok(await _userService.UpdateUserAsync(id, request));
}

[HttpPost("{id}/change-password")]
public async Task<IActionResult> ChangePassword(Guid id, [FromBody] ChangePasswordRequest request)
{
var currentUserId = User.FindFirstValue(ClaimTypes.NameIdentifier);
if (currentUserId != id.ToString())
return Forbid(); // только сам пользователь

await _userService.ChangePasswordAsync(id, request);
return NoContent();
}

[HttpPatch("{id}/role")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> UpdateRole(Guid id, [FromBody] UpdateUserRoleRequest request)
{
return Ok(await _userService.UpdateRoleAsync(id, request));
}

[HttpDelete("{id}")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> Delete(Guid id)
{
await _userService.DeleteUserAsync(id);
return NoContent();
}

[HttpPost("{id}/restore")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> Restore(Guid id)
{
await _userService.RestoreUserAsync(id);
return NoContent();
}
}
}
1 change: 1 addition & 0 deletions Library.API/Library.API.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

<ItemGroup>
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="12.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.20" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.18">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
Loading
Loading