Changeset b373fea
- Timestamp:
- 06/23/26 15:20:39 (12 days ago)
- Branches:
- main
- Children:
- 0b502c2
- Parents:
- d300631
- Files:
-
- 1 deleted
- 26 edited
-
ChapterX.API/Controllers/AdminsController.cs (modified) (3 diffs)
-
ChapterX.API/Controllers/ChaptersController.cs (modified) (4 diffs)
-
ChapterX.API/Controllers/CommentsController.cs (modified) (4 diffs)
-
ChapterX.API/Controllers/StoriesController.cs (modified) (4 diffs)
-
ChapterX.API/Controllers/UsersController.cs (modified) (3 diffs)
-
ChapterX.API/Program.cs (modified) (3 diffs)
-
ChapterX.Application/Auth/LoginRequest.cs (modified) (1 diff)
-
ChapterX.Application/Auth/RegisterRequest.cs (modified) (1 diff)
-
ChapterX.Application/Chapter/Commands/AddRequest.cs (modified) (1 diff)
-
ChapterX.Application/Chapter/Commands/DeleteHandler.cs (modified) (1 diff)
-
ChapterX.Application/Chapter/Commands/DeleteRequest.cs (modified) (1 diff)
-
ChapterX.Application/Chapter/Commands/UpdateHandler.cs (modified) (1 diff)
-
ChapterX.Application/Chapter/Commands/UpdateRequest.cs (modified) (1 diff)
-
ChapterX.Application/Comment/Commands/AddRequest.cs (modified) (1 diff)
-
ChapterX.Application/Comment/Commands/DeleteHandler.cs (modified) (1 diff)
-
ChapterX.Application/Comment/Commands/DeleteRequest.cs (modified) (1 diff)
-
ChapterX.Application/Comment/Commands/UpdateHandler.cs (modified) (1 diff)
-
ChapterX.Application/Comment/Commands/UpdateRequest.cs (modified) (1 diff)
-
ChapterX.Application/Story/Commands/AddRequest.cs (modified) (1 diff)
-
ChapterX.Application/Story/Commands/DeleteHandler.cs (modified) (1 diff)
-
ChapterX.Application/Story/Commands/DeleteRequest.cs (modified) (1 diff)
-
ChapterX.Application/Story/Commands/UpdateHandler.cs (modified) (1 diff)
-
ChapterX.Application/Story/Commands/UpdateRequest.cs (modified) (1 diff)
-
ChapterX.Domain/Repositories/IChapterRepository.cs (modified) (1 diff)
-
ChapterX.Domain/Shared/PasswordHasher.cs (deleted)
-
ChapterX.Infrastructure/Repositories/ChapterRepository.cs (modified) (1 diff)
-
ChapterX.Infrastructure/Services/JwtTokenService.cs (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
ChapterX.API/Controllers/AdminsController.cs
rd300631 rb373fea 40 40 41 41 [HttpPost] 42 [Authorize ]42 [Authorize(Roles = "Admin")] 43 43 public async Task<ActionResult> Add([FromBody] AddRequest request) 44 44 { … … 49 49 50 50 [HttpPut("{id:int}")] 51 [Authorize ]51 [Authorize(Roles = "Admin")] 52 52 public async Task<ActionResult> Update(int id, [FromBody] UpdateRequest request) 53 53 { … … 63 63 64 64 [HttpDelete("{id:int}")] 65 [Authorize ]65 [Authorize(Roles = "Admin")] 66 66 public async Task<ActionResult> Delete(int id) 67 67 { -
ChapterX.API/Controllers/ChaptersController.cs
rd300631 rb373fea 5 5 using Microsoft.AspNetCore.Mvc; 6 6 using Microsoft.Extensions.Logging; 7 using System.IdentityModel.Tokens.Jwt; 8 using System.Security.Claims; 7 9 8 10 namespace ChapterX.API.Controllers … … 40 42 41 43 [HttpPost] 44 [Authorize] 42 45 public async Task<ActionResult> Add([FromBody] AddRequest request) 43 46 { … … 57 60 } 58 61 59 var response = await _mediator.Send(request); 62 var callerId = int.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Sub)!); 63 var response = await _mediator.Send(request with { CallerId = callerId }); 60 64 return Ok(response); 61 65 } … … 66 70 { 67 71 _logger.LogInformation("Deleting chapter with ID: {ChapterId}", id); 68 var response = await _mediator.Send(new DeleteRequest(id)); 72 var callerId = int.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Sub)!); 73 var response = await _mediator.Send(new DeleteRequest(id, callerId)); 69 74 return Ok(response); 70 75 } -
ChapterX.API/Controllers/CommentsController.cs
rd300631 rb373fea 6 6 using Microsoft.AspNetCore.Mvc; 7 7 using Microsoft.Extensions.Logging; 8 using System.IdentityModel.Tokens.Jwt; 9 using System.Security.Claims; 8 10 9 11 namespace ChapterX.API.Controllers … … 63 65 public async Task<ActionResult> Add([FromBody] AddRequest request) 64 66 { 67 var callerId = int.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Sub)!); 65 68 _logger.LogInformation("Adding a new comment"); 66 var response = await _mediator.Send(request );69 var response = await _mediator.Send(request with { UserId = callerId }); 67 70 return Ok(response); 68 71 } … … 78 81 } 79 82 80 var response = await _mediator.Send(request); 83 var callerId = int.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Sub)!); 84 var response = await _mediator.Send(request with { CallerId = callerId }); 81 85 return Ok(response); 82 86 } … … 87 91 { 88 92 _logger.LogInformation("Deleting comment with ID: {CommentId}", id); 89 var response = await _mediator.Send(new DeleteRequest(id)); 93 var callerId = int.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Sub)!); 94 var response = await _mediator.Send(new DeleteRequest(id, callerId)); 90 95 return Ok(response); 91 96 } -
ChapterX.API/Controllers/StoriesController.cs
rd300631 rb373fea 5 5 using Microsoft.AspNetCore.Mvc; 6 6 using Microsoft.Extensions.Logging; 7 using System.IdentityModel.Tokens.Jwt; 8 using System.Security.Claims; 7 9 8 10 namespace ChapterX.API.Controllers … … 46 48 public async Task<ActionResult> Add([FromBody] AddRequest request) 47 49 { 48 _logger.LogInformation("Adding a new story for UserId: {UserId}", request.UserId); 49 var response = await _mediator.Send(request); 50 var callerId = int.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Sub)!); 51 _logger.LogInformation("Adding a new story for UserId: {UserId}", callerId); 52 var response = await _mediator.Send(request with { UserId = callerId }); 50 53 return Ok(response); 51 54 } … … 62 65 } 63 66 64 var response = await _mediator.Send(request); 67 var callerId = int.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Sub)!); 68 var response = await _mediator.Send(request with { CallerId = callerId }); 65 69 return Ok(response); 66 70 } … … 72 76 { 73 77 _logger.LogInformation("Deleting story with ID: {StoryId}", id); 74 var response = await _mediator.Send(new DeleteRequest(id)); 78 var callerId = int.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Sub)!); 79 var response = await _mediator.Send(new DeleteRequest(id, callerId)); 75 80 return Ok(response); 76 81 } -
ChapterX.API/Controllers/UsersController.cs
rd300631 rb373fea 5 5 using Microsoft.AspNetCore.Mvc; 6 6 using Microsoft.Extensions.Logging; 7 using System.IdentityModel.Tokens.Jwt; 8 using System.Security.Claims; 7 9 8 10 namespace ChapterX.API.Controllers … … 67 69 } 68 70 71 var callerId = int.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Sub)!); 72 var isAdmin = User.IsInRole("Admin"); 73 if (callerId != id && !isAdmin) 74 return Forbid(); 75 69 76 var response = await _mediator.Send(request); 70 77 return Ok(response); … … 76 83 { 77 84 _logger.LogInformation("Deleting user with ID: {UserId}", id); 85 var callerId = int.Parse(User.FindFirstValue(JwtRegisteredClaimNames.Sub)!); 86 var isAdmin = User.IsInRole("Admin"); 87 if (callerId != id && !isAdmin) 88 return Forbid(); 89 78 90 var response = await _mediator.Send(new DeleteRequest(id)); 79 91 return Ok(response); -
ChapterX.API/Program.cs
rd300631 rb373fea 7 7 8 8 var builder = WebApplication.CreateBuilder(args); 9 10 var jwtKey = builder.Configuration["Jwt:Key"]; 11 if (string.IsNullOrWhiteSpace(jwtKey) || jwtKey.StartsWith("change-this")) 12 throw new InvalidOperationException("Jwt:Key is not configured. Set it via environment variable DOTNET_Jwt__Key before starting the application."); 9 13 10 14 builder.Services.AddCors(options => … … 85 89 ctx.Response.ContentType = "application/json"; 86 90 91 var logger = ctx.RequestServices.GetRequiredService<ILogger<Program>>(); 92 87 93 string message; 88 94 int status; … … 107 113 message = "A user with this email or username already exists."; 108 114 else 109 message = "Database error: " + inner; 115 { 116 logger.LogError(dbEx, "Unhandled database error"); 117 message = "A database error occurred. Please try again."; 118 } 110 119 } 111 120 else 112 121 { 122 logger.LogError(ex, "Unhandled exception"); 113 123 status = 500; 114 message = ex?.Message ?? "Anerror occurred.";124 message = "An unexpected error occurred."; 115 125 } 116 126 -
ChapterX.Application/Auth/LoginRequest.cs
rd300631 rb373fea 1 1 using MediatR; 2 using System.ComponentModel.DataAnnotations; 2 3 3 4 namespace ChapterX.Application.Auth 4 5 { 5 public record LoginRequest(string Email, string Password) : IRequest<LoginResponse>; 6 public record LoginRequest( 7 [Required][EmailAddress][MaxLength(200)] string Email, 8 [Required][MaxLength(128)] string Password 9 ) : IRequest<LoginResponse>; 6 10 } -
ChapterX.Application/Auth/RegisterRequest.cs
rd300631 rb373fea 1 1 using MediatR; 2 using System.ComponentModel.DataAnnotations; 2 3 3 4 namespace ChapterX.Application.Auth 4 5 { 5 public record RegisterRequest(string Username, string Email, string Name, string Surname, string Password) : IRequest<RegisterResponse>; 6 public record RegisterRequest( 7 [Required][MaxLength(50)] string Username, 8 [Required][EmailAddress][MaxLength(200)] string Email, 9 [Required][MaxLength(100)] string Name, 10 [Required][MaxLength(100)] string Surname, 11 [Required][MinLength(8)][MaxLength(128)] string Password 12 ) : IRequest<RegisterResponse>; 6 13 } -
ChapterX.Application/Chapter/Commands/AddRequest.cs
rd300631 rb373fea 1 1 using MediatR; 2 using System.ComponentModel.DataAnnotations; 2 3 3 4 namespace ChapterX.Application.Chapter.Commands 4 5 { 5 6 public record AddRequest( 6 int Number,7 string Name,8 string Title,9 string Content,7 [Range(1, int.MaxValue)] int Number, 8 [Required][MaxLength(200)] string Name, 9 [Required][MaxLength(300)] string Title, 10 [Required] string Content, 10 11 int StoryId 11 12 ) : IRequest<AddResponse>; -
ChapterX.Application/Chapter/Commands/DeleteHandler.cs
rd300631 rb373fea 18 18 public async Task<DeleteResponse> Handle(DeleteRequest request, CancellationToken cancellationToken) 19 19 { 20 var chapter = await _chapterRepository.GetById Async(request.Id, cancellationToken);20 var chapter = await _chapterRepository.GetByIdWithStoryAsync(request.Id, cancellationToken); 21 21 if (chapter is null) 22 22 return new DeleteResponse(false); 23 24 if (chapter.Story.UserId != request.CallerId) 25 throw new UnauthorizedAccessException("You do not own this chapter."); 23 26 24 27 await _chapterRepository.DeleteAsync(chapter, cancellationToken); -
ChapterX.Application/Chapter/Commands/DeleteRequest.cs
rd300631 rb373fea 3 3 namespace ChapterX.Application.Chapter.Commands 4 4 { 5 public record DeleteRequest(int Id ) : IRequest<DeleteResponse>;5 public record DeleteRequest(int Id, int CallerId) : IRequest<DeleteResponse>; 6 6 } -
ChapterX.Application/Chapter/Commands/UpdateHandler.cs
rd300631 rb373fea 18 18 public async Task<UpdateResponse> Handle(UpdateRequest request, CancellationToken cancellationToken) 19 19 { 20 var chapter = await _chapterRepository.GetById Async(request.Id, cancellationToken);20 var chapter = await _chapterRepository.GetByIdWithStoryAsync(request.Id, cancellationToken); 21 21 if (chapter is null) 22 22 return new UpdateResponse(false); 23 24 if (chapter.Story.UserId != request.CallerId) 25 throw new UnauthorizedAccessException("You do not own this chapter."); 23 26 24 27 chapter.Number = request.Number; -
ChapterX.Application/Chapter/Commands/UpdateRequest.cs
rd300631 rb373fea 9 9 string Title, 10 10 string Content, 11 int? WordCount 11 int? WordCount, 12 int CallerId = 0 12 13 ) : IRequest<UpdateResponse>; 13 14 } -
ChapterX.Application/Comment/Commands/AddRequest.cs
rd300631 rb373fea 1 1 using MediatR; 2 using System.ComponentModel.DataAnnotations; 2 3 3 4 namespace ChapterX.Application.Comment.Commands 4 5 { 5 public record AddRequest(string Content, int UserId, int StoryId) : IRequest<AddResponse>; 6 public record AddRequest( 7 [Required][MaxLength(2000)] string Content, 8 int UserId, 9 int StoryId 10 ) : IRequest<AddResponse>; 6 11 } -
ChapterX.Application/Comment/Commands/DeleteHandler.cs
rd300631 rb373fea 22 22 return new DeleteResponse(false); 23 23 24 if (comment.UserId != request.CallerId) 25 throw new UnauthorizedAccessException("You do not own this comment."); 26 24 27 await _commentRepository.DeleteAsync(comment, cancellationToken); 25 28 -
ChapterX.Application/Comment/Commands/DeleteRequest.cs
rd300631 rb373fea 3 3 namespace ChapterX.Application.Comment.Commands 4 4 { 5 public record DeleteRequest(int Id ) : IRequest<DeleteResponse>;5 public record DeleteRequest(int Id, int CallerId) : IRequest<DeleteResponse>; 6 6 } -
ChapterX.Application/Comment/Commands/UpdateHandler.cs
rd300631 rb373fea 22 22 return new UpdateResponse(false); 23 23 24 if (comment.UserId != request.CallerId) 25 throw new UnauthorizedAccessException("You do not own this comment."); 26 24 27 comment.Content = request.Content; 25 28 comment.UpdatedAt = DateTime.UtcNow; -
ChapterX.Application/Comment/Commands/UpdateRequest.cs
rd300631 rb373fea 3 3 namespace ChapterX.Application.Comment.Commands 4 4 { 5 public record UpdateRequest(int Id, string Content ) : IRequest<UpdateResponse>;5 public record UpdateRequest(int Id, string Content, int CallerId = 0) : IRequest<UpdateResponse>; 6 6 } -
ChapterX.Application/Story/Commands/AddRequest.cs
rd300631 rb373fea 1 1 using MediatR; 2 using System.ComponentModel.DataAnnotations; 2 3 3 4 namespace ChapterX.Application.Story.Commands 4 5 { 5 public record AddRequest(bool MatureContent, string ShortDescription, string? Image, string Content, int UserId, List<string> Genres) : IRequest<AddResponse>; 6 public record AddRequest( 7 bool MatureContent, 8 [Required][MaxLength(500)] string ShortDescription, 9 [MaxLength(2048)] string? Image, 10 [Required] string Content, 11 int UserId, 12 List<string> Genres 13 ) : IRequest<AddResponse>; 6 14 } -
ChapterX.Application/Story/Commands/DeleteHandler.cs
rd300631 rb373fea 22 22 return new DeleteResponse(false); 23 23 24 if (story.UserId != request.CallerId) 25 throw new UnauthorizedAccessException("You do not own this story."); 26 24 27 await _storyRepository.DeleteAsync(story, cancellationToken); 25 28 -
ChapterX.Application/Story/Commands/DeleteRequest.cs
rd300631 rb373fea 3 3 namespace ChapterX.Application.Story.Commands 4 4 { 5 public record DeleteRequest(int Id ) : IRequest<DeleteResponse>;5 public record DeleteRequest(int Id, int CallerId) : IRequest<DeleteResponse>; 6 6 } -
ChapterX.Application/Story/Commands/UpdateHandler.cs
rd300631 rb373fea 22 22 return new UpdateResponse(false); 23 23 24 if (story.UserId != request.CallerId) 25 throw new UnauthorizedAccessException("You do not own this story."); 26 24 27 story.MatureContent = request.MatureContent; 25 28 story.ShortDescription = request.ShortDescription; -
ChapterX.Application/Story/Commands/UpdateRequest.cs
rd300631 rb373fea 3 3 namespace ChapterX.Application.Story.Commands 4 4 { 5 public record UpdateRequest(int Id, bool MatureContent, string ShortDescription, string? Image, string Content ) : IRequest<UpdateResponse>;5 public record UpdateRequest(int Id, bool MatureContent, string ShortDescription, string? Image, string Content, int CallerId = 0) : IRequest<UpdateResponse>; 6 6 } -
ChapterX.Domain/Repositories/IChapterRepository.cs
rd300631 rb373fea 6 6 { 7 7 Task<IEnumerable<Chapter>> GetByStoryIdAsync(int storyId, CancellationToken cancellationToken = default); 8 Task<Chapter?> GetByIdWithStoryAsync(int id, CancellationToken cancellationToken = default); 8 9 } 9 10 } -
ChapterX.Infrastructure/Repositories/ChapterRepository.cs
rd300631 rb373fea 24 24 .ToListAsync(cancellationToken); 25 25 } 26 27 public async Task<Chapter?> GetByIdWithStoryAsync(int id, CancellationToken cancellationToken = default) 28 { 29 return await _dbSet 30 .Include(c => c.Story) 31 .FirstOrDefaultAsync(c => c.Id == id, cancellationToken); 32 } 26 33 } 27 34 } -
ChapterX.Infrastructure/Services/JwtTokenService.cs
rd300631 rb373fea 23 23 var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); 24 24 25 var role = user.Admin != null ? "Admin" 26 : user.Writer != null ? "Writer" 27 : "RegularUser"; 28 25 29 var claims = new[] 26 30 { … … 28 32 new Claim(JwtRegisteredClaimNames.Email, user.Email), 29 33 new Claim(JwtRegisteredClaimNames.UniqueName, user.Username), 30 new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) 34 new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), 35 new Claim(ClaimTypes.Role, role) 31 36 }; 32 37
Note:
See TracChangeset
for help on using the changeset viewer.
