225 lines
8.5 KiB
C#
225 lines
8.5 KiB
C#
using Hangfire;
|
|
using Manager.Services;
|
|
using ManagerService.Data;
|
|
using ManagerService.DTOs;
|
|
using ManagerService.Services;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Logging;
|
|
using NSwag.Annotations;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace ManagerService.Controllers
|
|
{
|
|
[Authorize(Policy = ManagerService.Service.Security.Policies.InstanceAdmin)]
|
|
[ApiController, Route("api/[controller]")]
|
|
[OpenApiTag("Notification", Description = "Push notification management")]
|
|
public class NotificationController : ControllerBase
|
|
{
|
|
private readonly ILogger<NotificationController> _logger;
|
|
private readonly MyInfoMateDbContext _db;
|
|
private readonly NotificationService _notificationService;
|
|
IHexIdGeneratorService idService = new HexIdGeneratorService();
|
|
|
|
public NotificationController(
|
|
ILogger<NotificationController> logger,
|
|
MyInfoMateDbContext db,
|
|
NotificationService notificationService)
|
|
{
|
|
_logger = logger;
|
|
_db = db;
|
|
_notificationService = notificationService;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get all notifications for the current instance
|
|
/// </summary>
|
|
[ProducesResponseType(typeof(System.Collections.Generic.List<PushNotificationDTO>), 200)]
|
|
[ProducesResponseType(typeof(string), 500)]
|
|
[HttpGet]
|
|
public async Task<ObjectResult> Get()
|
|
{
|
|
try
|
|
{
|
|
var instanceId = User.FindFirst(ManagerService.Service.Security.ClaimTypes.InstanceId)?.Value;
|
|
|
|
var notifications = await _db.PushNotifications
|
|
.Where(n => n.InstanceId == instanceId)
|
|
.OrderByDescending(n => n.DateCreation)
|
|
.ToListAsync();
|
|
|
|
return new OkObjectResult(notifications.Select(n => n.ToDTO()));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new ObjectResult(ex.Message) { StatusCode = 500 };
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Send a push notification immediately to all devices of the instance
|
|
/// </summary>
|
|
[ProducesResponseType(typeof(PushNotificationDTO), 200)]
|
|
[ProducesResponseType(typeof(string), 400)]
|
|
[ProducesResponseType(typeof(string), 500)]
|
|
[HttpPost("send")]
|
|
public async Task<ObjectResult> Send([FromBody] SendNotificationDTO dto)
|
|
{
|
|
try
|
|
{
|
|
if (dto == null)
|
|
throw new ArgumentNullException("Notification param is null");
|
|
|
|
var instanceId = User.FindFirst(ManagerService.Service.Security.ClaimTypes.InstanceId)?.Value;
|
|
|
|
var instance = await _db.Instances.FirstOrDefaultAsync(i => i.Id == instanceId);
|
|
if (instance == null || !instance.IsPushNotification)
|
|
throw new InvalidOperationException("Push notifications are not enabled for this instance");
|
|
|
|
var notification = new PushNotification
|
|
{
|
|
Id = idService.GenerateHexId(),
|
|
InstanceId = instanceId,
|
|
Title = dto.title,
|
|
Body = dto.body,
|
|
Topic = $"instance_{instanceId}",
|
|
Status = PushNotificationStatus.Scheduled,
|
|
DateCreation = DateTime.UtcNow
|
|
};
|
|
|
|
_db.PushNotifications.Add(notification);
|
|
await _db.SaveChangesAsync();
|
|
|
|
var jobId = BackgroundJob.Enqueue<NotificationService>(s => s.SendToTopicAsync(notification.Id));
|
|
notification.HangfireJobId = jobId;
|
|
await _db.SaveChangesAsync();
|
|
|
|
return new OkObjectResult(notification.ToDTO());
|
|
}
|
|
catch (ArgumentNullException ex)
|
|
{
|
|
return new BadRequestObjectResult(ex.Message);
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
return new ConflictObjectResult(ex.Message);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new ObjectResult(ex.Message) { StatusCode = 500 };
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Schedule a push notification for a future date/time
|
|
/// </summary>
|
|
[ProducesResponseType(typeof(PushNotificationDTO), 200)]
|
|
[ProducesResponseType(typeof(string), 400)]
|
|
[ProducesResponseType(typeof(string), 500)]
|
|
[HttpPost("schedule")]
|
|
public async Task<ObjectResult> Schedule([FromBody] ScheduleNotificationDTO dto)
|
|
{
|
|
try
|
|
{
|
|
if (dto == null)
|
|
throw new ArgumentNullException("Notification param is null");
|
|
|
|
if (dto.scheduledAt <= DateTime.UtcNow)
|
|
throw new ArgumentException("Scheduled date must be in the future");
|
|
|
|
var instanceId = User.FindFirst(ManagerService.Service.Security.ClaimTypes.InstanceId)?.Value;
|
|
|
|
var instance = await _db.Instances.FirstOrDefaultAsync(i => i.Id == instanceId);
|
|
if (instance == null || !instance.IsPushNotification)
|
|
throw new InvalidOperationException("Push notifications are not enabled for this instance");
|
|
|
|
var notification = new PushNotification
|
|
{
|
|
Id = idService.GenerateHexId(),
|
|
InstanceId = instanceId,
|
|
Title = dto.title,
|
|
Body = dto.body,
|
|
Topic = $"instance_{instanceId}",
|
|
Status = PushNotificationStatus.Scheduled,
|
|
ScheduledAt = dto.scheduledAt.ToUniversalTime(),
|
|
DateCreation = DateTime.UtcNow
|
|
};
|
|
|
|
_db.PushNotifications.Add(notification);
|
|
await _db.SaveChangesAsync();
|
|
|
|
var delay = dto.scheduledAt.ToUniversalTime() - DateTime.UtcNow;
|
|
var jobId = BackgroundJob.Schedule<NotificationService>(s => s.SendToTopicAsync(notification.Id), delay);
|
|
notification.HangfireJobId = jobId;
|
|
await _db.SaveChangesAsync();
|
|
|
|
return new OkObjectResult(notification.ToDTO());
|
|
}
|
|
catch (ArgumentNullException ex)
|
|
{
|
|
return new BadRequestObjectResult(ex.Message);
|
|
}
|
|
catch (ArgumentException ex)
|
|
{
|
|
return new BadRequestObjectResult(ex.Message);
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
return new ConflictObjectResult(ex.Message);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new ObjectResult(ex.Message) { StatusCode = 500 };
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Cancel a scheduled notification
|
|
/// </summary>
|
|
[ProducesResponseType(typeof(string), 202)]
|
|
[ProducesResponseType(typeof(string), 404)]
|
|
[ProducesResponseType(typeof(string), 500)]
|
|
[HttpDelete("{id}")]
|
|
public async Task<ObjectResult> Cancel(string id)
|
|
{
|
|
try
|
|
{
|
|
var instanceId = User.FindFirst(ManagerService.Service.Security.ClaimTypes.InstanceId)?.Value;
|
|
|
|
var notification = await _db.PushNotifications
|
|
.FirstOrDefaultAsync(n => n.Id == id && n.InstanceId == instanceId);
|
|
|
|
if (notification == null)
|
|
throw new KeyNotFoundException("Notification not found");
|
|
|
|
if (notification.Status != PushNotificationStatus.Scheduled)
|
|
throw new InvalidOperationException("Only scheduled notifications can be cancelled");
|
|
|
|
if (notification.HangfireJobId != null)
|
|
BackgroundJob.Delete(notification.HangfireJobId);
|
|
|
|
_db.PushNotifications.Remove(notification);
|
|
await _db.SaveChangesAsync();
|
|
|
|
return new ObjectResult("Notification cancelled") { StatusCode = 202 };
|
|
}
|
|
catch (KeyNotFoundException ex)
|
|
{
|
|
return new NotFoundObjectResult(ex.Message);
|
|
}
|
|
catch (InvalidOperationException ex)
|
|
{
|
|
return new ConflictObjectResult(ex.Message);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new ObjectResult(ex.Message) { StatusCode = 500 };
|
|
}
|
|
}
|
|
}
|
|
}
|