-
Notifications
You must be signed in to change notification settings - Fork 2
Notification System Implementation #20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
888cfb3
ad90d92
47f9e2b
fe6259a
6c8330e
0f1a29c
e65c410
7432403
9125016
065fdac
b2dca6d
fda6755
bf3cefd
d7bd9cf
3e53d29
75057f0
cb39a85
dcc35e8
73b3fba
7e45bdb
ef94e30
97620e4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| using System.Security.Claims; | ||
| using AskFm.BLL.DTO; | ||
| using AskFm.BLL.Services; | ||
| using AskFm.DAL.Enums; | ||
| using Microsoft.AspNetCore.Authorization; | ||
| using Microsoft.AspNetCore.Mvc; | ||
|
|
||
| namespace AskFm.API.Controllers | ||
| { | ||
| [ApiController] | ||
| [Route("api/[controller]")] | ||
| [Authorize] | ||
| public class NotificationController : ControllerBase | ||
| { | ||
| private readonly INotificationService _notificationService; | ||
|
|
||
| public NotificationController(INotificationService notificationService) | ||
| { | ||
| _notificationService = notificationService; | ||
| } | ||
|
|
||
| [HttpGet] | ||
| public async Task<IActionResult> GetUserNotifications([FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 10) | ||
| { | ||
| try | ||
| { | ||
| var userId = GetCurrentUserId(); | ||
| var notifications = await _notificationService.GetUserNotifications(userId, pageNumber, pageSize); | ||
| return Ok(notifications); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| return BadRequest(ex.Message); | ||
| } | ||
| } | ||
|
|
||
| [HttpGet("type/{category}")] | ||
| public async Task<IActionResult> GetNotificationsByType(string category, [FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 10) | ||
| { | ||
| try | ||
| { | ||
| var userId = GetCurrentUserId(); | ||
| var response = await _notificationService.GetNotificationsByType(userId, category, pageNumber, pageSize); | ||
| return Ok(response); | ||
| } | ||
| catch (ArgumentException ex) | ||
| { | ||
| return BadRequest(ex.Message); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| return StatusCode(500, ex.Message); | ||
| } | ||
| } | ||
|
|
||
| [HttpPut("{notificationId}/read")] | ||
| public async Task<IActionResult> MarkNotificationAsRead(int notificationId) | ||
| { | ||
| try | ||
| { | ||
| var userId = GetCurrentUserId(); | ||
| var result = await _notificationService.MarkNotificationAsRead(notificationId, userId); | ||
| return Ok(new { message = result }); | ||
| } | ||
| catch (InvalidOperationException ex) | ||
| { | ||
| return NotFound(ex.Message); | ||
| } | ||
| catch (UnauthorizedAccessException ex) | ||
| { | ||
| return Forbid(ex.Message); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| return BadRequest(ex.Message); | ||
| } | ||
| } | ||
|
|
||
| [HttpPut("read-all")] | ||
| public async Task<IActionResult> MarkAllNotificationsAsRead() | ||
| { | ||
| try | ||
| { | ||
| var userId = GetCurrentUserId(); | ||
| var result = await _notificationService.MarkAllNotificationsAsRead(userId); | ||
| return Ok(new { message = result }); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| return BadRequest(ex.Message); | ||
| } | ||
| } | ||
|
|
||
| [HttpPost] | ||
| [Authorize(Roles = "Admin")] | ||
| public async Task<IActionResult> CreateNotification([FromBody] CreateNotificationRequest request) | ||
| { | ||
| try | ||
| { | ||
| await _notificationService.CreateNotification(request.UserId, request.Type, request.ResourceId, request.Message); | ||
| return Ok(new { message = "Notification created successfully" }); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| return BadRequest(ex.Message); | ||
| } | ||
| } | ||
|
|
||
| private int GetCurrentUserId() | ||
| { | ||
| // Use the standard NameIdentifier claim | ||
| var userIdClaim = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; | ||
| if (string.IsNullOrEmpty(userIdClaim) || !int.TryParse(userIdClaim, out int userId)) | ||
| { | ||
| throw new UnauthorizedAccessException("Invalid user token"); | ||
| } | ||
| return userId; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,19 +1,19 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net9.0</TargetFramework> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\AskFm.DAL\AskFm.DAL.csproj"/> | ||
| <ProjectReference Include="..\Shared\Shared.csproj"/> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="AutoMapper" Version="15.0.1" /> | ||
| <PropertyGroup> | ||
| <TargetFramework>net9.0</TargetFramework> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <ProjectReference Include="..\AskFm.DAL\AskFm.DAL.csproj"/> | ||
| <ProjectReference Include="..\Shared\Shared.csproj"/> | ||
| </ItemGroup> | ||
| <ItemGroup> | ||
| <PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.2.0"/> | ||
| <PackageReference Include="AutoMapper" Version="15.0.1"/> | ||
| <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1"/> | ||
| <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.0" /> | ||
| </ItemGroup> | ||
|
|
||
| </ItemGroup> | ||
|
|
||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| namespace AskFm.BLL.DTO; | ||
|
|
||
| public class ActorDto | ||
| { | ||
| public int Id { get; set; } | ||
| public string Username { get; set; } | ||
| public string AvatarPath { get; set; } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Threading.Tasks; | ||
| using AskFm.DAL.Enums; | ||
|
|
||
| namespace AskFm.BLL.DTO | ||
| { | ||
| public class CreateNotificationRequest | ||
| { | ||
| public int UserId { get; set; } | ||
| public NotificationStatus Type { get; set; } | ||
| public int ResourceId { get; set; } | ||
| public string Message { get; set; } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,19 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| using AskFm.BLL.DTO; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public class NotificationDto | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public int Id { get; set; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public string Type { get; set; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public string Message { get; set; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public bool IsRead { get; set; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public DateTime CreatedAt { get; set; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public int ResourceId { get; set; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public int UserId { get; set; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public ActorDto? Actor { get; set; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public PaginationDto Pagination { get; set; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+3
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing namespace; class is in the global namespace This will break consumers expecting AskFm.BLL.DTO.NotificationDto. Apply: -public class NotificationDto
-{
+namespace AskFm.BLL.DTO;
+
+public class NotificationDto
+{Also initialize non-nullable strings to satisfy nullability: - public string Type { get; set; }
+ public string Type { get; set; } = default!;
- public string Message { get; set; }
+ public string Message { get; set; } = default!;📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| namespace AskFm.BLL.DTO; | ||
|
|
||
| public class PaginationDto | ||
| { | ||
| public int CurrentPage { get; set; } | ||
| public int TotalPages { get; set; } | ||
| public int TotalCount { get; set; } | ||
| public bool HasNext { get; set; } | ||
| public bool HasPrevious { get; set; } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don’t ship AllowAnyOrigin; gate CORS by environment and allow credentials for known origins.
As-is, any site can open a WebSocket to your hub in production. Tighten CORS.
📝 Committable suggestion
🤖 Prompt for AI Agents