Added the firebase services
This commit is contained in:
parent
47a3d6035c
commit
58b817be99
@ -102,6 +102,8 @@ namespace Marco.Pms.DataAccess.Data
|
|||||||
public DbSet<StatusPermissionMapping> StatusPermissionMapping { get; set; }
|
public DbSet<StatusPermissionMapping> StatusPermissionMapping { get; set; }
|
||||||
public DbSet<ExpensesStatusMapping> ExpensesStatusMapping { get; set; }
|
public DbSet<ExpensesStatusMapping> ExpensesStatusMapping { get; set; }
|
||||||
|
|
||||||
|
public DbSet<FCMTokenMapping> FCMTokenMappings { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
4471
Marco.Pms.DataAccess/Migrations/20250813060211_Added_FCMTokenMApping_Table.Designer.cs
generated
Normal file
4471
Marco.Pms.DataAccess/Migrations/20250813060211_Added_FCMTokenMApping_Table.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Marco.Pms.DataAccess.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class Added_FCMTokenMApping_Table : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "FCMTokenMappings",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
EmployeeId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||||
|
FcmToken = table.Column<string>(type: "longtext", nullable: false)
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||||
|
TenantId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci")
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_FCMTokenMappings", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_FCMTokenMappings_Tenants_TenantId",
|
||||||
|
column: x => x.TenantId,
|
||||||
|
principalTable: "Tenants",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
})
|
||||||
|
.Annotation("MySql:CharSet", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_FCMTokenMappings_TenantId",
|
||||||
|
table: "FCMTokenMappings",
|
||||||
|
column: "TenantId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "FCMTokenMappings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3058,6 +3058,29 @@ namespace Marco.Pms.DataAccess.Migrations
|
|||||||
b.ToTable("JobRoles");
|
b.ToTable("JobRoles");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Marco.Pms.Model.Utilities.FCMTokenMapping", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<Guid>("EmployeeId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.Property<string>("FcmToken")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<Guid>("TenantId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("TenantId");
|
||||||
|
|
||||||
|
b.ToTable("FCMTokenMappings");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Marco.Pms.Model.Utilities.Inquiries", b =>
|
modelBuilder.Entity("Marco.Pms.Model.Utilities.Inquiries", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@ -4368,6 +4391,17 @@ namespace Marco.Pms.DataAccess.Migrations
|
|||||||
b.Navigation("Tenant");
|
b.Navigation("Tenant");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Marco.Pms.Model.Utilities.FCMTokenMapping", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Marco.Pms.Model.Entitlements.Tenant", "Tenant")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TenantId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Tenant");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
{
|
{
|
||||||
public class FCMTokenMapping : TenantRelation
|
public class FCMTokenMapping : TenantRelation
|
||||||
{
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
public Guid EmployeeId { get; set; }
|
public Guid EmployeeId { get; set; }
|
||||||
public string FCcmToken { get; set; } = string.Empty;
|
|
||||||
public string FcmToken { get; set; } = string.Empty;
|
public string FcmToken { get; set; } = string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
using FirebaseAdmin.Messaging;
|
using Marco.Pms.DataAccess.Data;
|
||||||
using Marco.Pms.DataAccess.Data;
|
|
||||||
using Marco.Pms.Model.Authentication;
|
using Marco.Pms.Model.Authentication;
|
||||||
using Marco.Pms.Model.Dtos.Authentication;
|
using Marco.Pms.Model.Dtos.Authentication;
|
||||||
using Marco.Pms.Model.Dtos.Util;
|
using Marco.Pms.Model.Dtos.Util;
|
||||||
using Marco.Pms.Model.Employees;
|
using Marco.Pms.Model.Employees;
|
||||||
using Marco.Pms.Model.Entitlements;
|
using Marco.Pms.Model.Entitlements;
|
||||||
using Marco.Pms.Model.Utilities;
|
using Marco.Pms.Model.Utilities;
|
||||||
|
using Marco.Pms.Services.Service.ServiceInterfaces;
|
||||||
using MarcoBMS.Services.Helpers;
|
using MarcoBMS.Services.Helpers;
|
||||||
using MarcoBMS.Services.Service;
|
using MarcoBMS.Services.Service;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
@ -32,9 +32,10 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
private readonly IConfiguration _configuration;
|
private readonly IConfiguration _configuration;
|
||||||
private readonly EmployeeHelper _employeeHelper;
|
private readonly EmployeeHelper _employeeHelper;
|
||||||
private readonly ILoggingService _logger;
|
private readonly ILoggingService _logger;
|
||||||
|
private readonly IFirebaseService _firebase;
|
||||||
//string tenentId = "1";
|
//string tenentId = "1";
|
||||||
public AuthController(UserManager<ApplicationUser> userManager, ApplicationDbContext context, JwtSettings jwtSettings, RefreshTokenService refreshTokenService,
|
public AuthController(UserManager<ApplicationUser> userManager, ApplicationDbContext context, JwtSettings jwtSettings, RefreshTokenService refreshTokenService,
|
||||||
IEmailSender emailSender, IConfiguration configuration, EmployeeHelper employeeHelper, UserHelper userHelper, ILoggingService logger)
|
IEmailSender emailSender, IConfiguration configuration, EmployeeHelper employeeHelper, UserHelper userHelper, ILoggingService logger, IFirebaseService firebase)
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_jwtSettings = jwtSettings;
|
_jwtSettings = jwtSettings;
|
||||||
@ -45,6 +46,7 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
_context = context;
|
_context = context;
|
||||||
_userHelper = userHelper;
|
_userHelper = userHelper;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_firebase = firebase;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("login")]
|
[HttpPost("login")]
|
||||||
@ -222,42 +224,40 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
// Return a successful response with the generated tokens.
|
// Return a successful response with the generated tokens.
|
||||||
_logger.LogInfo("User {Username} logged in successfully.", user.UserName);
|
_logger.LogInfo("User {Username} logged in successfully.", user.UserName);
|
||||||
|
|
||||||
|
var exsistingFCMMapping = await _context.FCMTokenMappings.FirstOrDefaultAsync(ft => ft.EmployeeId == emp.Id);
|
||||||
|
if (exsistingFCMMapping == null)
|
||||||
|
{
|
||||||
|
var fcmTokenMapping = new FCMTokenMapping
|
||||||
|
{
|
||||||
|
EmployeeId = emp.Id,
|
||||||
|
FcmToken = loginDto.FcmToken,
|
||||||
|
TenantId = emp.TenantId
|
||||||
|
};
|
||||||
|
_context.FCMTokenMappings.Add(fcmTokenMapping);
|
||||||
|
_logger.LogInfo("New FCM Token registering for employee {EmployeeId}", emp.Id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
exsistingFCMMapping.FcmToken = loginDto.FcmToken;
|
||||||
|
_logger.LogInfo("Updating FCM Token for employee {EmployeeId}", emp.Id);
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Exception occured while saving FCM Token for employee {EmployeeId}", emp.Id);
|
||||||
|
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Error", ex.Message, 500));
|
||||||
|
}
|
||||||
|
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
// --- Push Notification Section ---
|
// --- Push Notification Section ---
|
||||||
// This section attempts to send a test push notification to the user's device.
|
// This section attempts to send a test push notification to the user's device.
|
||||||
// It's designed to fail gracefully and handle invalid Firebase Cloud Messaging (FCM) tokens.
|
// It's designed to fail gracefully and handle invalid Firebase Cloud Messaging (FCM) tokens.
|
||||||
var message = new Message()
|
await _firebase.SendMessageToMultipleDevicesAsync();
|
||||||
{
|
|
||||||
Token = loginDto.FcmToken,
|
|
||||||
Notification = new Notification
|
|
||||||
{
|
|
||||||
Title = "Hello from AuthController",
|
|
||||||
Body = "This is a test with not increased response time message"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Attempt to send the message via Firebase.
|
|
||||||
string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
|
|
||||||
_logger.LogInfo("Successfully sent test push notification. MessageId: {MessageId}", response);
|
|
||||||
}
|
|
||||||
catch (FirebaseMessagingException ex)
|
|
||||||
{
|
|
||||||
// Log the specific Firebase error.
|
|
||||||
_logger.LogError(ex, "Error sending push notification");
|
|
||||||
|
|
||||||
// Check for specific error codes that indicate an invalid or unregistered token.
|
|
||||||
if (ex.MessagingErrorCode == MessagingErrorCode.Unregistered ||
|
|
||||||
ex.MessagingErrorCode == MessagingErrorCode.InvalidArgument)
|
|
||||||
{
|
|
||||||
_logger.LogWarning("FCM token is invalid and should be deleted from the database: {Token}", loginDto.FcmToken ?? string.Empty);
|
|
||||||
|
|
||||||
// TODO: Implement the logic here to remove the invalid token from your database.
|
|
||||||
// Example: await YourTokenService.DeleteTokenAsync(loginDto.DeviceToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return Ok(ApiResponse<object>.SuccessResponse(responseData, "User logged in successfully.", 200));
|
return Ok(ApiResponse<object>.SuccessResponse(responseData, "User logged in successfully.", 200));
|
||||||
}
|
}
|
||||||
@ -899,22 +899,34 @@ namespace MarcoBMS.Services.Controllers
|
|||||||
{
|
{
|
||||||
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
|
||||||
var tenantId = _userHelper.GetTenantId();
|
var tenantId = _userHelper.GetTenantId();
|
||||||
var fcmTokenMapping = new FCMTokenMapping
|
|
||||||
|
var exsistingFCMMapping = await _context.FCMTokenMappings.FirstOrDefaultAsync(ft => ft.EmployeeId == loggedInEmployee.Id);
|
||||||
|
if (exsistingFCMMapping == null)
|
||||||
{
|
{
|
||||||
EmployeeId = loggedInEmployee.Id,
|
var fcmTokenMapping = new FCMTokenMapping
|
||||||
FcmToken = model.FcmToken,
|
{
|
||||||
TenantId = tenantId
|
EmployeeId = loggedInEmployee.Id,
|
||||||
};
|
FcmToken = model.FcmToken,
|
||||||
//_context.FCMTokenMappings.Add(fcmTokenMapping);
|
TenantId = tenantId
|
||||||
//try
|
};
|
||||||
//{
|
_context.FCMTokenMappings.Add(fcmTokenMapping);
|
||||||
// await _context.SaveChangesAsync();
|
_logger.LogInfo("New FCM Token registering for employee {EmployeeId}", loggedInEmployee.Id);
|
||||||
//}
|
}
|
||||||
//catch (Exception ex)
|
else
|
||||||
//{
|
{
|
||||||
// _logger.LogError(ex, "Exception occured while saving FCM Token for employee {EmployeeId}", loggedInEmployee.Id);
|
exsistingFCMMapping.FcmToken = model.FcmToken;
|
||||||
//}
|
_logger.LogInfo("Updating FCM Token for employee {EmployeeId}", loggedInEmployee.Id);
|
||||||
return Ok(ApiResponse<object>.SuccessResponse(fcmTokenMapping));
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Exception occured while saving FCM Token for employee {EmployeeId}", loggedInEmployee.Id);
|
||||||
|
return StatusCode(500, ApiResponse<object>.ErrorResponse("Internal Error", ex.Message, 500));
|
||||||
|
}
|
||||||
|
return Ok(ApiResponse<object>.SuccessResponse(new { }, "FCM Token registered Successfuly", 200));
|
||||||
}
|
}
|
||||||
private static string ComputeSha256Hash(string rawData)
|
private static string ComputeSha256Hash(string rawData)
|
||||||
{
|
{
|
||||||
|
@ -178,6 +178,7 @@ builder.Services.AddScoped<ISignalRService, SignalRService>();
|
|||||||
builder.Services.AddScoped<IProjectServices, ProjectServices>();
|
builder.Services.AddScoped<IProjectServices, ProjectServices>();
|
||||||
builder.Services.AddScoped<IExpensesService, ExpensesService>();
|
builder.Services.AddScoped<IExpensesService, ExpensesService>();
|
||||||
builder.Services.AddScoped<IMasterService, MasterService>();
|
builder.Services.AddScoped<IMasterService, MasterService>();
|
||||||
|
builder.Services.AddScoped<IFirebaseService, FirebaseService>();
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Helpers
|
#region Helpers
|
||||||
|
90
Marco.Pms.Services/Service/FirebaseService.cs
Normal file
90
Marco.Pms.Services/Service/FirebaseService.cs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
using FirebaseAdmin.Messaging;
|
||||||
|
using Marco.Pms.DataAccess.Data;
|
||||||
|
using Marco.Pms.Services.Service.ServiceInterfaces;
|
||||||
|
using MarcoBMS.Services.Service;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace Marco.Pms.Services.Service
|
||||||
|
{
|
||||||
|
public class FirebaseService : IFirebaseService
|
||||||
|
{
|
||||||
|
private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;
|
||||||
|
private readonly ILoggingService _logger;
|
||||||
|
public FirebaseService(IDbContextFactory<ApplicationDbContext> dbContextFactory,
|
||||||
|
ILoggingService logger)
|
||||||
|
{
|
||||||
|
_dbContextFactory = dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory));
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SendDemoMessages(Notification notificationFirebase)
|
||||||
|
{
|
||||||
|
string deviceToken = "";
|
||||||
|
var message = new Message()
|
||||||
|
{
|
||||||
|
Token = deviceToken,
|
||||||
|
Notification = notificationFirebase
|
||||||
|
};
|
||||||
|
string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SendMessageToMultipleDevicesAsync()
|
||||||
|
{
|
||||||
|
await using var _context = await _dbContextFactory.CreateDbContextAsync();
|
||||||
|
|
||||||
|
// List of device registration tokens to send the message to
|
||||||
|
var registrationTokens = await _context.FCMTokenMappings.Select(ft => ft.FcmToken).ToListAsync();
|
||||||
|
//var registrationTokens = new List<string>
|
||||||
|
//{
|
||||||
|
// "YOUR_REGISTRATION_TOKEN_1",
|
||||||
|
// "YOUR_REGISTRATION_TOKEN_2",
|
||||||
|
// // add up to 500 tokens
|
||||||
|
//};
|
||||||
|
|
||||||
|
var message = new MulticastMessage()
|
||||||
|
{
|
||||||
|
Tokens = registrationTokens,
|
||||||
|
Notification = new Notification
|
||||||
|
{
|
||||||
|
Title = "Testing from API",
|
||||||
|
Body = "This messages comes from FireBase Services"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Send the multicast message
|
||||||
|
var response = await FirebaseMessaging.DefaultInstance.SendEachForMulticastAsync(message);
|
||||||
|
_logger.LogInfo("{SuccessCount} messages were sent successfully.", response.SuccessCount);
|
||||||
|
|
||||||
|
if (response.FailureCount > 0)
|
||||||
|
{
|
||||||
|
var failedTokens = new List<string>();
|
||||||
|
for (int i = 0; i < response.Responses.Count; i++)
|
||||||
|
{
|
||||||
|
if (!response.Responses[i].IsSuccess)
|
||||||
|
{
|
||||||
|
failedTokens.Add(registrationTokens[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_logger.LogInfo("List of tokens that caused failures: " + string.Join(", ", failedTokens));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (FirebaseMessagingException ex)
|
||||||
|
{
|
||||||
|
// Log the specific Firebase error.
|
||||||
|
_logger.LogError(ex, "Error sending push notification");
|
||||||
|
|
||||||
|
// Check for specific error codes that indicate an invalid or unregistered token.
|
||||||
|
if (ex.MessagingErrorCode == MessagingErrorCode.Unregistered ||
|
||||||
|
ex.MessagingErrorCode == MessagingErrorCode.InvalidArgument)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("FCM token is invalid and should be deleted from the database");
|
||||||
|
|
||||||
|
// TODO: Implement the logic here to remove the invalid token from your database.
|
||||||
|
// Example: await YourTokenService.DeleteTokenAsync(loginDto.DeviceToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace Marco.Pms.Services.Service.ServiceInterfaces
|
||||||
|
{
|
||||||
|
public interface IFirebaseService
|
||||||
|
{
|
||||||
|
Task SendMessageToMultipleDevicesAsync();
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user