Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,8 @@ TestResult.xml
nunit-*.xml


appsettings.Development.json
appsettings.Development.json
appsettings.json
*appsettings.json

.env
Binary file not shown.
Binary file not shown.
Binary file added .vs/Fin-Backend/DesignTimeBuild/.dtbcache.v2
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added .vs/Fin-Backend/v17/.futdcache.v2
Binary file not shown.
Binary file added .vs/Fin-Backend/v17/.suo
Binary file not shown.
394 changes: 394 additions & 0 deletions .vs/Fin-Backend/v17/DocumentLayout.json

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file added .vs/ProjectEvaluation/fin-backend.strings.v9.bin
Binary file not shown.
6 changes: 6 additions & 0 deletions Fin.Api/Fin.Api.csproj.user
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>https</ActiveDebugProfile>
</PropertyGroup>
</Project>
8 changes: 4 additions & 4 deletions Fin.Api/Program.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Fin.Application.Notifications.Extensions;
using Fin.Infrastructure.Constants;
using Fin.Infrastructure.Extensions;
using Fin.Infrastructure.Notifications.Hubs;
using Fin.Infrastructure.Seeders.Extensions;
using Hangfire;

var builder = WebApplication.CreateBuilder(args);
Expand All @@ -10,7 +10,6 @@

builder.Services
.AddInfrastructure(builder.Configuration)
.AddNotifications()
.AddOpenApiDocument()
.AddCors(options =>
{
Expand All @@ -19,7 +18,8 @@
{
policy.WithOrigins(frontEndUrl)
.AllowAnyHeader()
.AllowAnyMethod();
.AllowAnyMethod()
.AllowCredentials();
});
})
.AddControllers();
Expand All @@ -36,9 +36,9 @@
app.UseCors("AllowAngularLocalhost");
}


app.UseNotifications();
app.UseFinMiddlewares();
await app.UseSeeders();

app.UseAuthentication();
app.UseAuthorization();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public static class UseNotificationExtension
{
public static void UseNotifications(this WebApplication app)
{
app.MapHub<NotificationHub>("/notifications");
app.MapHub<NotificationHub>("/notifications-hub");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Notification = FirebaseAdmin.Messaging.Notification;

namespace Fin.Application.Notifications.Services.DeliveryServices;
Expand All @@ -32,51 +33,66 @@ public class NotificationDeliveryService(
IRepository<UserNotificationSettings> userSettingsRepository,
IConfiguration configuration,
IAmbientData ambientData,
IDateTimeProvider _dateTimeProvider,

IDateTimeProvider dateTimeProvider,
IHubContext<NotificationHub> hubContext,
IEmailSenderService emailSenderService,
IFirebaseNotificationService firebaseNotification
)
IFirebaseNotificationService firebaseNotification,
ILogger<NotificationDeliveryService> logger)
: INotificationDeliveryService, IAutoTransient
{
private readonly string SEND_NOTIFICATION_ACTION = "ReceiveNotification";

private readonly CryptoHelper _cryptoHelper = new(
configuration.GetSection(AuthenticationConstants.EncryptKeyConfigKey).Value ?? "",
configuration.GetSection(AuthenticationConstants.EncryptIvConfigKey).Value ?? ""
);
);


public async Task SendNotification(NotifyUserDto notifyUser, bool autoSave = true)
{
var notificationDelivery = await deliveryRepository.Query()
.FirstOrDefaultAsync(n => n.NotificationId == notifyUser.NotificationId && n.UserId == notifyUser.UserId);
if (notificationDelivery == null)
throw new Exception($"Notification not found to send. NotificationId {notifyUser.NotificationId}, UserId {notifyUser.UserId}");
throw new Exception(
$"Notification not found to send. NotificationId {notifyUser.NotificationId}, UserId {notifyUser.UserId}");

var userSettings = await userSettingsRepository.Query()
.FirstOrDefaultAsync(u => u.UserId == notifyUser.UserId);
if (userSettings == null)
throw new Exception($"User Notification Settings not found to send. NotificationId {notifyUser.NotificationId}, UserId {notifyUser.UserId}");
throw new Exception(
$"User Notification Settings not found to send. NotificationId {notifyUser.NotificationId}, UserId {notifyUser.UserId}");

foreach (var way in notifyUser.Ways)
{
if (way is NotificationWay.Snack or NotificationWay.Message)
{
await hubContext.Clients.User(notifyUser.UserId.ToString()).SendAsync(SEND_NOTIFICATION_ACTION, notifyUser);
continue;
}
var allowedWaysToSend = userSettings.AllowedWays.Intersect(notifyUser.Ways).ToList();

if (!userSettings.Enabled || !userSettings.AllowedWays.Contains(way)) continue;
if (!userSettings.Enabled || !allowedWaysToSend.Any())
{
logger.LogWarning(
"User {userId}, don't get notification {notificationId} on any way because notifications is disabled or any way is allowed.",
notifyUser.UserId, notifyUser.NotificationId);
return;
}

if (way is NotificationWay.Push)
await SendPush(notifyUser, userSettings, false);
if (way is NotificationWay.Email)
await SendEmail(notifyUser);
try
{
if (
allowedWaysToSend.Contains(NotificationWay.Snack)
| allowedWaysToSend.Contains(NotificationWay.Message)
| allowedWaysToSend.Contains(NotificationWay.Push)
) await SendWebSocket(notifyUser);
if (allowedWaysToSend.Contains(NotificationWay.Push))
await SendFirebase(notifyUser, userSettings, false);
if (allowedWaysToSend.Contains(NotificationWay.Email))
await SendEmail(notifyUser);
notificationDelivery.MarkAsDelivered();
}
catch (Exception e)
{
logger.LogError(
"Error on send notification with id {notificationId} to user id: {userId}.\nError: {err}",
notifyUser.NotificationId, notifyUser.UserId, e.ToString());
}

notificationDelivery.MarkAsDelivered();
await deliveryRepository.UpdateAsync(notificationDelivery, false);
await deliveryRepository.UpdateAsync(notificationDelivery);

if (autoSave)
await deliveryRepository.SaveChangesAsync();
Expand All @@ -87,7 +103,7 @@ public async Task<bool> MarkAsVisualized(Guid notificationId, bool autoSave = tr
if (!ambientData.IsLogged)
throw new UnauthorizedAccessException("User not logged");

var userId = ambientData.UserId;
var userId = ambientData.UserId.Value;
var notification = await deliveryRepository.Query()
.FirstOrDefaultAsync(n => n.NotificationId == notificationId && n.UserId == userId);
if (notification == null)
Expand All @@ -100,37 +116,48 @@ public async Task<bool> MarkAsVisualized(Guid notificationId, bool autoSave = tr

public async Task<List<NotifyUserDto>> GetUnvisualizedNotifications(bool autoSave = true)
{
var userId = ambientData.UserId;
var now = _dateTimeProvider.UtcNow();
if (!ambientData.IsLogged)
throw new UnauthorizedAccessException("User not logged");

var userId = ambientData.UserId.Value;
var now = dateTimeProvider.UtcNow();

var notifications = await deliveryRepository.Query(tracking: false)
var userNotification = await deliveryRepository.Query(tracking: false)
.Include(u => u.Notification)
.Where(n => !n.Visualized && n.UserId == userId)
.Where(n => n.Notification.StartToDelivery <= now)
.Where(n => n.Notification.StartToDelivery <= now.AddMinutes(1))
.Where(n => !n.Notification.StopToDelivery.HasValue ||
n.Notification.StopToDelivery.Value >= now)
.ToListAsync();
var userNotification = notifications
.Where(n => n.Notification.Ways.Contains(NotificationWay.Push) ||
n.Notification.Ways.Contains(NotificationWay.Message) ||
n.Notification.Ways.Contains(NotificationWay.Snack))
.Select(n => new NotifyUserDto(n.Notification, n))
.ToListAsync();

userNotification = userNotification
.Where(n => n.Ways.Any(n => n != NotificationWay.Email))
.ToList();

var notificationToMarkAsDelivery = userNotification
.Select(u => u.NotificationId)
.ToList();

await deliveryRepository.Query()
.Where(n => userNotification.Select(u => u.NotificationId).Contains(n.NotificationId))
.Where(n => notificationToMarkAsDelivery.Contains(n.NotificationId))
.ExecuteUpdateAsync(x => x
.SetProperty(a => a.Visualized, true));
.SetProperty(a => a.Delivery, true));

if (autoSave) await deliveryRepository.SaveChangesAsync();

return userNotification;
}

private async Task SendPush(NotifyUserDto notify, UserNotificationSettings userSettings, bool autoSave)
private async Task SendWebSocket(NotifyUserDto notifyUser)
{
await hubContext.Clients.User(userSettings.UserId.ToString()).SendAsync(SEND_NOTIFICATION_ACTION, notify);
await hubContext.Clients.User(notifyUser.UserId.ToString()).SendAsync(SEND_NOTIFICATION_ACTION, notifyUser);
}

private async Task SendFirebase(NotifyUserDto notify, UserNotificationSettings userSettings, bool autoSave)
{
if (userSettings.FirebaseTokens is not { Count: > 0 }) return;

var messages = userSettings.FirebaseTokens
.Select(t => new Message
{
Expand All @@ -140,6 +167,8 @@ private async Task SendPush(NotifyUserDto notify, UserNotificationSettings userS
{ "htmlBody", notify.HtmlBody },
{ "textBody", notify.TextBody },
{ "notificationId", notify.NotificationId.ToString() },
{ "severity", notify.Severity.ToString() },
{ "link", notify.Link },
},
Notification = new Notification
{
Expand All @@ -165,7 +194,7 @@ private async Task SendEmail(NotifyUserDto notification)
var userCredencial = await credencialRepository.Query(false)
.FirstOrDefaultAsync(n => n.UserId == notification.UserId);
if (userCredencial == null)
throw new Exception($"User not found to send email notification. UserId {notification.UserId}, NotificationId {notification.NotificationId}");
throw new Exception("User not found to send email notification.");

var email = _cryptoHelper.Decrypt(userCredencial.EncryptedEmail);
await emailSenderService.SendEmailAsync(email, notification.Title, notification.HtmlBody);
Expand Down
40 changes: 21 additions & 19 deletions Fin.Application/Users/Services/UserCreateService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Fin.Domain.Tenants.Entities;
using Fin.Domain.Users.Dtos;
using Fin.Domain.Users.Entities;
using Fin.Domain.Users.Factories;
using Fin.Infrastructure.Authentications.Constants;
using Fin.Infrastructure.AutoServices.Interfaces;
using Fin.Infrastructure.Constants;
Expand Down Expand Up @@ -197,23 +198,23 @@ public async Task<ValidationResultDto<UserDto>> CreateUser(string creationToken,
var now = _dateTimeProvider.UtcNow();

var user = new User(input, now);
var credential = new UserCredential(user.Id, process.EncryptedEmail, process.EncryptedPassword);
var credential = UserCredentialFactory.Create(user.Id, process.EncryptedEmail, process.EncryptedPassword, UserCredentialFactoryType.Password);

var tenant = new Tenant(now);
user.Tenants.Add(tenant);

var notificationSetting = new UserNotificationSettings(user.Id, tenant.Id);
var rememberUseSetting = new UserRememberUseSetting(user.Id, tenant.Id);

await _unitOfWork.BeginTransactionAsync();

await _tenantRepository.AddAsync(tenant);
await _userRepository.AddAsync(user);
await _credentialRepository.AddAsync(credential);
await _userRememberUseSettingRepository.AddAsync(rememberUseSetting);
await _notificationSettingsRepository.AddAsync(notificationSetting);
await _unitOfWork.CommitAsync();

await using (await _unitOfWork.BeginTransactionAsync())
{
await _tenantRepository.AddAsync(tenant);
await _userRepository.AddAsync(user);
await _credentialRepository.AddAsync(credential);
await _userRememberUseSettingRepository.AddAsync(rememberUseSetting);
await _notificationSettingsRepository.AddAsync(notificationSetting);
await _unitOfWork.CommitAsync();
}

await _cache.RemoveAsync(GenerateProcessCacheKey(creationToken));

Expand Down Expand Up @@ -241,22 +242,23 @@ public async Task<ValidationResultDto<UserDto>> CreateUser(string googleId, stri
var now = _dateTimeProvider.UtcNow();

var user = new User(input, now);
var credential = UserCredential.CreateWithGoogle(user.Id, encryptedEmail, googleId);
var credential = UserCredentialFactory.Create(user.Id, encryptedEmail, googleId, UserCredentialFactoryType.Google);

var tenant = new Tenant(now);
user.Tenants.Add(tenant);

var notificationSetting = new UserNotificationSettings(user.Id, tenant.Id);
var rememberUseSetting = new UserRememberUseSetting(user.Id, tenant.Id);

await _unitOfWork.BeginTransactionAsync();
await _tenantRepository.AddAsync(tenant);
await _userRepository.AddAsync(user);
await _credentialRepository.AddAsync(credential);
await _userRememberUseSettingRepository.AddAsync(rememberUseSetting);
await _notificationSettingsRepository.AddAsync(notificationSetting);
await _unitOfWork.CommitAsync();

await using (await _unitOfWork.BeginTransactionAsync())
{
await _tenantRepository.AddAsync(tenant);
await _userRepository.AddAsync(user);
await _credentialRepository.AddAsync(credential);
await _userRememberUseSettingRepository.AddAsync(rememberUseSetting);
await _notificationSettingsRepository.AddAsync(notificationSetting);
await _unitOfWork.CommitAsync();
}

user.Tenants.First().Users = null;
user.Credential.User = null;
Expand Down
39 changes: 21 additions & 18 deletions Fin.Application/Users/Services/UserDeleteService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,31 +153,34 @@ private async Task DeleteUser(Guid userId, CancellationToken cancellationToken =
var notifications = notificationDeliveries.Select(n => n.Notification)
.Where(n => n.UserDeliveries.Count == 1);

await unitOfWork.BeginTransactionAsync(cancellationToken);
await using (await unitOfWork.BeginTransactionAsync(cancellationToken))
{

foreach (var notification in notifications)
await notificationRepo.DeleteAsync(notification, cancellationToken);
foreach (var delivery in notificationDeliveries)
await notificationDeliveryRepo.DeleteAsync(delivery, cancellationToken);
foreach (var notification in notifications)
await notificationRepo.DeleteAsync(notification, cancellationToken);
foreach (var delivery in notificationDeliveries)
await notificationDeliveryRepo.DeleteAsync(delivery, cancellationToken);

await rememberRepo.DeleteAsync(rememberSetting, cancellationToken);
await notificationSettingsRepo.DeleteAsync(notificationSetting, cancellationToken);
await rememberRepo.DeleteAsync(rememberSetting, cancellationToken);
await notificationSettingsRepo.DeleteAsync(notificationSetting, cancellationToken);

foreach (var otherDeleteRequest in otherDeleteRequests)
await userDeleteRequestRepo.DeleteAsync(otherDeleteRequest, cancellationToken);
foreach (var otherDeleteRequest in otherDeleteRequests)
await userDeleteRequestRepo.DeleteAsync(otherDeleteRequest, cancellationToken);

foreach (var tenantUser in tenantsUser)
await tenantUserRepo.DeleteAsync(tenantUser, cancellationToken);
foreach (var tenantUser in tenantsUser)
await tenantUserRepo.DeleteAsync(tenantUser, cancellationToken);

foreach (var tenant in tenants)
await tenantRepo.DeleteAsync(tenant, cancellationToken);
foreach (var tenant in tenants)
await tenantRepo.DeleteAsync(tenant, cancellationToken);

await credentialRepo.DeleteAsync(credential, cancellationToken);
await userDeleteRequestRepo.DeleteAsync(deleteRequest, cancellationToken);
await userRepo.DeleteAsync(user, cancellationToken);
await credentialRepo.DeleteAsync(credential, cancellationToken);
await userDeleteRequestRepo.DeleteAsync(deleteRequest, cancellationToken);
await userRepo.DeleteAsync(user, cancellationToken);

await emailSender.SendEmailAsync(userEmail, "Conta deletada", "Sua conta no FinApp foi deletada. Agora você não poderá mais acessar seus dados e eles foram removidos da plataforma.");
await emailSender.SendEmailAsync(userEmail, "Conta deletada",
"Sua conta no FinApp foi deletada. Agora você não poderá mais acessar seus dados e eles foram removidos da plataforma.");

await unitOfWork.CommitAsync(cancellationToken);
await unitOfWork.CommitAsync(cancellationToken);
}
}
}
4 changes: 1 addition & 3 deletions Fin.Domain/Global/Interfaces/ITenantEntity.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System.ComponentModel.DataAnnotations;

namespace Fin.Domain.Global.Interfaces;
namespace Fin.Domain.Global.Interfaces;

public interface ITenantEntity: IEntity
{
Expand Down
2 changes: 2 additions & 0 deletions Fin.Domain/Notifications/Dtos/NotificationInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ public class NotificationInput
public DateTime StartToDelivery { get; set; }
public DateTime? StopToDelivery { get; set; }
public List<Guid> UserIds { get; set; } = new();
public string Link { get; set; }
public NotificationSeverity Severity { get; set; }
}
Loading
Loading