165 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			165 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System.ComponentModel.DataAnnotations;
 | |
| using DysonNetwork.Shared.Auth;
 | |
| using DysonNetwork.Shared.Models;
 | |
| using DysonNetwork.Shared.Proto;
 | |
| using Microsoft.AspNetCore.Authorization;
 | |
| using Microsoft.AspNetCore.Mvc;
 | |
| using Microsoft.EntityFrameworkCore;
 | |
| using NodaTime;
 | |
| 
 | |
| namespace DysonNetwork.Ring.Notification;
 | |
| 
 | |
| [ApiController]
 | |
| [Route("/api/notifications")]
 | |
| public class NotificationController(
 | |
|     AppDatabase db,
 | |
|     PushService nty
 | |
| ) : ControllerBase
 | |
| {
 | |
|     [HttpGet("count")]
 | |
|     [Authorize]
 | |
|     public async Task<ActionResult<int>> CountUnreadNotifications()
 | |
|     {
 | |
|         HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
 | |
|         if (currentUserValue is not Account currentUser) return Unauthorized();
 | |
|         var accountId = Guid.Parse(currentUser.Id);
 | |
| 
 | |
|         var count = await db.Notifications
 | |
|             .Where(s => s.AccountId == accountId && s.ViewedAt == null)
 | |
|             .CountAsync();
 | |
|         return Ok(count);
 | |
|     }
 | |
| 
 | |
|     [HttpGet]
 | |
|     [Authorize]
 | |
|     public async Task<ActionResult<List<SnNotification>>> ListNotifications(
 | |
|         [FromQuery] int offset = 0,
 | |
|         // The page size set to 5 is to avoid the client pulled the notification
 | |
|         // but didn't render it in the screen-viewable region.
 | |
|         [FromQuery] int take = 8
 | |
|     )
 | |
|     {
 | |
|         HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
 | |
|         if (currentUserValue is not Account currentUser) return Unauthorized();
 | |
|         var accountId = Guid.Parse(currentUser.Id);
 | |
| 
 | |
|         var totalCount = await db.Notifications
 | |
|             .Where(s => s.AccountId == accountId)
 | |
|             .CountAsync();
 | |
|         var notifications = await db.Notifications
 | |
|             .Where(s => s.AccountId == accountId)
 | |
|             .OrderByDescending(e => e.CreatedAt)
 | |
|             .Skip(offset)
 | |
|             .Take(take)
 | |
|             .ToListAsync();
 | |
| 
 | |
|         Response.Headers["X-Total"] = totalCount.ToString();
 | |
|         await nty.MarkNotificationsViewed(notifications.ToList());
 | |
| 
 | |
|         return Ok(notifications);
 | |
|     }
 | |
| 
 | |
|     [HttpPost("all/read")]
 | |
|     [Authorize]
 | |
|     public async Task<ActionResult> MarkAllNotificationsViewed()
 | |
|     {
 | |
|         HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
 | |
|         if (currentUserValue is not Account currentUser) return Unauthorized();
 | |
|         var accountId = Guid.Parse(currentUser.Id);
 | |
| 
 | |
|         await nty.MarkAllNotificationsViewed(accountId);
 | |
|         return Ok();
 | |
|     }
 | |
| 
 | |
|     public class PushNotificationSubscribeRequest
 | |
|     {
 | |
|         [MaxLength(4096)] public string DeviceToken { get; set; } = null!;
 | |
|         public PushProvider Provider { get; set; }
 | |
|     }
 | |
| 
 | |
|     [HttpPut("subscription")]
 | |
|     [Authorize]
 | |
|     public async Task<ActionResult<SnNotificationPushSubscription>>
 | |
|         SubscribeToPushNotification(
 | |
|             [FromBody] PushNotificationSubscribeRequest request
 | |
|         )
 | |
|     {
 | |
|         HttpContext.Items.TryGetValue("CurrentSession", out var currentSessionValue);
 | |
|         HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
 | |
|         var currentUser = currentUserValue as Account;
 | |
|         if (currentUser == null) return Unauthorized();
 | |
|         var currentSession = currentSessionValue as AuthSession;
 | |
|         if (currentSession == null) return Unauthorized();
 | |
| 
 | |
|         var result =
 | |
|             await nty.SubscribeDevice(
 | |
|                 currentSession.Challenge.DeviceId,
 | |
|                 request.DeviceToken,
 | |
|                 request.Provider,
 | |
|                 currentUser
 | |
|             );
 | |
| 
 | |
|         return Ok(result);
 | |
|     }
 | |
| 
 | |
|     [HttpDelete("subscription")]
 | |
|     [Authorize]
 | |
|     public async Task<ActionResult<int>> UnsubscribeFromPushNotification()
 | |
|     {
 | |
|         HttpContext.Items.TryGetValue("CurrentSession", out var currentSessionValue);
 | |
|         HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
 | |
|         var currentUser = currentUserValue as Account;
 | |
|         if (currentUser == null) return Unauthorized();
 | |
|         var currentSession = currentSessionValue as AuthSession;
 | |
|         if (currentSession == null) return Unauthorized();
 | |
|         var accountId = Guid.Parse(currentUser.Id);
 | |
| 
 | |
|         var affectedRows = await db.PushSubscriptions
 | |
|             .Where(s =>
 | |
|                 s.AccountId == accountId &&
 | |
|                 s.DeviceId == currentSession.Challenge.DeviceId
 | |
|             ).ExecuteDeleteAsync();
 | |
|         return Ok(affectedRows);
 | |
|     }
 | |
| 
 | |
|     public class NotificationRequest
 | |
|     {
 | |
|         [Required][MaxLength(1024)] public string Topic { get; set; } = null!;
 | |
|         [Required][MaxLength(1024)] public string Title { get; set; } = null!;
 | |
|         [MaxLength(2048)] public string? Subtitle { get; set; }
 | |
|         [Required][MaxLength(4096)] public string Content { get; set; } = null!;
 | |
|         public Dictionary<string, object?>? Meta { get; set; }
 | |
|         public int Priority { get; set; } = 10;
 | |
|     }
 | |
| 
 | |
|     public class NotificationWithAimRequest : NotificationRequest
 | |
|     {
 | |
|         [Required] public List<Guid> AccountId { get; set; } = null!;
 | |
|     }
 | |
| 
 | |
|     [HttpPost("send")]
 | |
|     [Authorize]
 | |
|     [RequiredPermission("global", "notifications.send")]
 | |
|     public async Task<ActionResult> SendNotification(
 | |
|         [FromBody] NotificationWithAimRequest request,
 | |
|         [FromQuery] bool save = false
 | |
|     )
 | |
|     {
 | |
|         await nty.SendNotificationBatch(
 | |
|             new SnNotification
 | |
|             {
 | |
|                 CreatedAt = SystemClock.Instance.GetCurrentInstant(),
 | |
|                 UpdatedAt = SystemClock.Instance.GetCurrentInstant(),
 | |
|                 Topic = request.Topic,
 | |
|                 Title = request.Title,
 | |
|                 Subtitle = request.Subtitle,
 | |
|                 Content = request.Content,
 | |
|                 Meta = request.Meta ?? [],
 | |
|             },
 | |
|             request.AccountId,
 | |
|             save
 | |
|         );
 | |
|         return Ok();
 | |
|     }
 | |
| }
 |