Add push notification
This commit is contained in:
parent
a452f4af04
commit
bad25bf5b3
224
ManagerService/Controllers/NotificationController.cs
Normal file
224
ManagerService/Controllers/NotificationController.cs
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
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 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
ManagerService/DTOs/PushNotificationDTO.cs
Normal file
31
ManagerService/DTOs/PushNotificationDTO.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using ManagerService.Data;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ManagerService.DTOs
|
||||||
|
{
|
||||||
|
public class PushNotificationDTO
|
||||||
|
{
|
||||||
|
public string? id { get; set; }
|
||||||
|
public string instanceId { get; set; }
|
||||||
|
public string title { get; set; }
|
||||||
|
public string body { get; set; }
|
||||||
|
public string topic { get; set; }
|
||||||
|
public PushNotificationStatus status { get; set; }
|
||||||
|
public DateTime? scheduledAt { get; set; }
|
||||||
|
public DateTime? sentAt { get; set; }
|
||||||
|
public DateTime dateCreation { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SendNotificationDTO
|
||||||
|
{
|
||||||
|
public string title { get; set; }
|
||||||
|
public string body { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ScheduleNotificationDTO
|
||||||
|
{
|
||||||
|
public string title { get; set; }
|
||||||
|
public string body { get; set; }
|
||||||
|
public DateTime scheduledAt { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -45,6 +45,9 @@ namespace ManagerService.Data
|
|||||||
// API Keys
|
// API Keys
|
||||||
public DbSet<ApiKey> ApiKeys { get; set; }
|
public DbSet<ApiKey> ApiKeys { get; set; }
|
||||||
|
|
||||||
|
// Push Notifications
|
||||||
|
public DbSet<PushNotification> PushNotifications { get; set; }
|
||||||
|
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
|
|||||||
59
ManagerService/Data/PushNotification.cs
Normal file
59
ManagerService/Data/PushNotification.cs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
using ManagerService.DTOs;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace ManagerService.Data
|
||||||
|
{
|
||||||
|
[Index(nameof(InstanceId))]
|
||||||
|
public class PushNotification
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string InstanceId { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string Title { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string Body { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string Topic { get; set; }
|
||||||
|
|
||||||
|
public PushNotificationStatus Status { get; set; }
|
||||||
|
|
||||||
|
public DateTime? ScheduledAt { get; set; }
|
||||||
|
|
||||||
|
public DateTime? SentAt { get; set; }
|
||||||
|
|
||||||
|
public string? HangfireJobId { get; set; }
|
||||||
|
|
||||||
|
public DateTime DateCreation { get; set; } = DateTime.UtcNow;
|
||||||
|
|
||||||
|
public PushNotificationDTO ToDTO()
|
||||||
|
{
|
||||||
|
return new PushNotificationDTO
|
||||||
|
{
|
||||||
|
id = Id,
|
||||||
|
instanceId = InstanceId,
|
||||||
|
title = Title,
|
||||||
|
body = Body,
|
||||||
|
topic = Topic,
|
||||||
|
status = Status,
|
||||||
|
scheduledAt = ScheduledAt,
|
||||||
|
sentAt = SentAt,
|
||||||
|
dateCreation = DateCreation
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PushNotificationStatus
|
||||||
|
{
|
||||||
|
Scheduled,
|
||||||
|
Sent,
|
||||||
|
Failed
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -21,6 +21,9 @@
|
|||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.3" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.3" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite" Version="9.0.3" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite" Version="9.0.3" />
|
||||||
<PackageReference Include="NSwag.AspNetCore" Version="13.10.8" />
|
<PackageReference Include="NSwag.AspNetCore" Version="13.10.8" />
|
||||||
|
<PackageReference Include="FirebaseAdmin" Version="3.1.0" />
|
||||||
|
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.17" />
|
||||||
|
<PackageReference Include="Hangfire.PostgreSql" Version="1.20.10" />
|
||||||
<PackageReference Include="Scrypt.NET" Version="1.3.0" />
|
<PackageReference Include="Scrypt.NET" Version="1.3.0" />
|
||||||
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
|
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
|
||||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.2.0" />
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.2.0" />
|
||||||
|
|||||||
1389
ManagerService/Migrations/20260316114312_AddPushNotifications.Designer.cs
generated
Normal file
1389
ManagerService/Migrations/20260316114312_AddPushNotifications.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace ManagerService.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddPushNotifications : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PushNotifications",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(type: "text", nullable: false),
|
||||||
|
InstanceId = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Title = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Body = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Topic = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Status = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
ScheduledAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||||
|
SentAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
|
||||||
|
HangfireJobId = table.Column<string>(type: "text", nullable: true),
|
||||||
|
DateCreation = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PushNotifications", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PushNotifications_InstanceId",
|
||||||
|
table: "PushNotifications",
|
||||||
|
column: "InstanceId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PushNotifications");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -332,6 +332,49 @@ namespace ManagerService.Migrations
|
|||||||
b.ToTable("Instances");
|
b.ToTable("Instances");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ManagerService.Data.PushNotification", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Body")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime>("DateCreation")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("HangfireJobId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("InstanceId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("ScheduledAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("SentAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<int>("Status")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Topic")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("InstanceId");
|
||||||
|
|
||||||
|
b.ToTable("PushNotifications");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ManagerService.Data.Resource", b =>
|
modelBuilder.Entity("ManagerService.Data.Resource", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("Id")
|
b.Property<string>("Id")
|
||||||
|
|||||||
@ -0,0 +1,17 @@
|
|||||||
|
using Hangfire.Dashboard;
|
||||||
|
|
||||||
|
namespace ManagerService.Security
|
||||||
|
{
|
||||||
|
public class HangfireDashboardAuthorizationFilter : IDashboardAuthorizationFilter
|
||||||
|
{
|
||||||
|
public bool Authorize(DashboardContext context)
|
||||||
|
{
|
||||||
|
var httpContext = context.GetHttpContext();
|
||||||
|
var user = httpContext.User;
|
||||||
|
|
||||||
|
return user.Identity?.IsAuthenticated == true &&
|
||||||
|
user.HasClaim(ManagerService.Service.Security.ClaimTypes.Permission,
|
||||||
|
ManagerService.Service.Security.Permissions.SuperAdmin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
67
ManagerService/Services/NotificationService.cs
Normal file
67
ManagerService/Services/NotificationService.cs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
using FirebaseAdmin;
|
||||||
|
using FirebaseAdmin.Messaging;
|
||||||
|
using Google.Apis.Auth.OAuth2;
|
||||||
|
using Hangfire;
|
||||||
|
using Manager.Services;
|
||||||
|
using ManagerService.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace ManagerService.Services
|
||||||
|
{
|
||||||
|
public class NotificationService
|
||||||
|
{
|
||||||
|
private readonly ILogger<NotificationService> _logger;
|
||||||
|
private readonly IServiceScopeFactory _scopeFactory;
|
||||||
|
|
||||||
|
public NotificationService(ILogger<NotificationService> logger, IServiceScopeFactory scopeFactory)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_scopeFactory = scopeFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SendToTopicAsync(string notificationId)
|
||||||
|
{
|
||||||
|
using var scope = _scopeFactory.CreateScope();
|
||||||
|
var db = scope.ServiceProvider.GetRequiredService<MyInfoMateDbContext>();
|
||||||
|
IHexIdGeneratorService idService = new HexIdGeneratorService();
|
||||||
|
|
||||||
|
var notification = await db.PushNotifications.FirstOrDefaultAsync(n => n.Id == notificationId);
|
||||||
|
if (notification == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Notification {Id} not found", notificationId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var message = new Message
|
||||||
|
{
|
||||||
|
Topic = notification.Topic,
|
||||||
|
Notification = new Notification
|
||||||
|
{
|
||||||
|
Title = notification.Title,
|
||||||
|
Body = notification.Body
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await FirebaseMessaging.DefaultInstance.SendAsync(message);
|
||||||
|
|
||||||
|
notification.Status = PushNotificationStatus.Sent;
|
||||||
|
notification.SentAt = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to send notification {Id}", notificationId);
|
||||||
|
notification.Status = PushNotificationStatus.Failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user