was added firebase nitifying

This commit is contained in:
Artemy
2026-03-01 16:48:06 +07:00
parent 76d7280c71
commit eab0d746b8
60 changed files with 2335 additions and 1149 deletions
@@ -10,6 +10,7 @@ using Govor.Application.Interfaces.Friends;
using Govor.Application.Interfaces.Infrastructure.Extensions;
using Govor.Application.Interfaces.Medias;
using Govor.Application.Interfaces.Messages;
using Govor.Application.Interfaces.PushNotifications;
using Govor.Application.Interfaces.UserOnlineStatus;
using Govor.Application.Interfaces.UserSession;
using Govor.Application.Interfaces.UserSession.Crypto;
@@ -18,6 +19,8 @@ using Govor.Application.Services.Authentication;
using Govor.Application.Services.Friends;
using Govor.Application.Services.Medias;
using Govor.Application.Services.Messages;
using Govor.Application.Services.PushNotifications;
using Govor.Application.Services.PushNotifications.Providers;
using Govor.Application.Services.UserOnlineStatus;
using Govor.Application.Services.UserSessions;
using Govor.Application.Services.UserSessions.Crypto;
@@ -33,6 +36,7 @@ using Govor.Core.Repositories.Invaites;
using Govor.Core.Repositories.MediasAttachments;
using Govor.Core.Repositories.Messages;
using Govor.Core.Repositories.PrivateChats;
using Govor.Core.Repositories.PushTokens;
using Govor.Core.Repositories.Users;
using Govor.Core.Repositories.UserSessionsRepository;
using Govor.Data;
@@ -45,7 +49,7 @@ public static class ConfigurationProgramExtensions
{
public static void AddServices(this IServiceCollection services)
{
services.AddScoped<IPasswordHasher, PasswordHasher>();
services.AddSingleton<IPasswordHasher, PasswordHasher>();
services.AddScoped<IJwtTokenHasher, JwtTokenHasher>();
services.AddScoped<IJwtService, JwtService>();
services.AddScoped<IAccountService, AuthService>();
@@ -74,10 +78,12 @@ public static class ConfigurationProgramExtensions
services.AddMemoryCache();
services.AddScoped<IPingHandlerService, PingHandlerService>();
services.AddScoped<IUserGroupsGetterService, UserGroupsGetterService>();
services.AddScoped<IMessageCommandService, MessageCommandService>();
services.AddScoped<IVerifyFriendship, VerifyFriendship>();
services.AddScoped<IUserGroupsGetterService, UserGroupsGetterService>();
services.AddScoped<IUserPrivateChatsGetterService, UserPrivateChatsGetter>();
services.AddScoped<IUserPrivateChatsCreator, UserPrivateChatsCreator>();
services.AddScoped<IMessagesLoader, MessagesLoader>();
services.AddScoped<IMediaService, MediaService>();
services.AddScoped<IAccesserToDownloadMedia, AccesserToDownloadMediaService>();
@@ -85,15 +91,23 @@ public static class ConfigurationProgramExtensions
// UserSession
services.AddScoped<IUserSessionOpener, UserSessionOpener>();
services.AddScoped<IUserSessionRefresher, UserSessionRefresher>();
services.AddScoped<IUserNotificationScopeService, UserNotificationScopeService>();
services.AddScoped<IUserPresenceReader, UserPresenceReader>();
services.AddSingleton<IOnlineUserStore, OnlineUserStore>();
// Hubs Infrastructure
services.AddScoped<IPrivateChatGroupManager, PrivateChatGroupManager>();
services.AddScoped<IChatNotificationService, ChatNotificationService>();
services.AddScoped<IConnectionManager, ConnectionManager>();
services.AddSingleton<IConnectionStore, ConnectionStore>();
// Pushs
services.AddScoped<IPushNotificationService, PushNotificationService>();
services.AddSingleton<IPushNotificationProvider, FirebasePushProvider>();
// Auto Mapper
services.AddAutoMapper(typeof(MappingProfile));
@@ -120,6 +134,7 @@ public static class ConfigurationProgramExtensions
services.AddScoped<IPrivateChatsRepository, PrivateChatsRepository>();
services.AddScoped<IGroupsRepository, GroupRepository>();
services.AddScoped<IUserSessionsRepository, UserSessionsRepository>();
services.AddScoped<IPushTokenRepository, PushTokenRepository>();
// other
}
@@ -160,6 +175,4 @@ public static class ConfigurationProgramExtensions
});
}
}
}
+31 -17
View File
@@ -1,5 +1,6 @@
using Govor.Application.Interfaces;
using Govor.Application.Interfaces.Infrastructure.Extensions;
using Govor.Contracts.DTOs;
using Govor.Core.Models;
using Govor.Core.Repositories.PrivateChats;
using Govor.Core.Repositories.Users;
@@ -17,7 +18,8 @@ public class PrivateChatController : Controller
private readonly ICurrentUserService _currentUser;
private readonly IUsersRepository _usersRepository;
private readonly IVerifyFriendship _verifyFriendship;
private readonly IPrivateChatsRepository _privateChats;
private readonly IUserPrivateChatsCreator _userPrivateChatsCreator;
private readonly IUserPrivateChatsGetterService _privateChatsGetter;
private readonly ILogger<ChatLoadController> _logger;
public PrivateChatController(
@@ -25,12 +27,15 @@ public class PrivateChatController : Controller
IUsersRepository usersRepository,
IVerifyFriendship verifyFriendship,
IPrivateChatsRepository privateChats,
IUserPrivateChatsCreator userPrivateChatsCreator,
IUserPrivateChatsGetterService userPrivateChatsGetterService,
ILogger<ChatLoadController> logger)
{
_currentUser = currentUser;
_usersRepository = usersRepository;
_verifyFriendship = verifyFriendship;
_privateChats = privateChats;
_userPrivateChatsCreator = userPrivateChatsCreator;
_privateChatsGetter = userPrivateChatsGetterService;
_logger = logger;
}
@@ -49,9 +54,8 @@ public class PrivateChatController : Controller
await _verifyFriendship.VerifyAsync(currentId, friendId);
var chatId = await GetPrivateChatsIdAsync(currentId, friendId);
return Ok(chatId);
var chat = await _userPrivateChatsCreator.CreateAsync(currentId, friendId);
return Ok(chat.Id);
}
catch (UnauthorizedAccessException ex)
{
@@ -74,22 +78,32 @@ public class PrivateChatController : Controller
return StatusCode(500, "Unexpected Error! Please try again later.");
}
}
private async Task<Guid> GetPrivateChatsIdAsync(Guid userA, Guid userB)
[HttpGet("user/private-chats")]
public async Task<IActionResult> GetChatsByFriends()
{
Guid recipientId;
// Makes new PrivateChat if not exist
if (!_privateChats.Exist(userA, userB))
try
{
recipientId = Guid.NewGuid();
await _privateChats.AddAsync(new PrivateChat()
{ Id = recipientId, UserAId = userA, UserBId = userB });
var currentId = _currentUser.GetCurrentUserId();
var chats = await _privateChatsGetter.GetUserChatsAsync(currentId);
var result = chats.Select(chat => new PrivateChatDto
{
ChatId = chat.Id,
FriendId = chat.UserAId == currentId ? chat.UserBId : chat.UserAId
}).ToList();
return Ok(result);
}
else
catch (UnauthorizedAccessException ex)
{
recipientId = (await _privateChats.GetByMembersAsync(userA, userB)).Id;
_logger.LogWarning(ex.Message);
return Forbid(ex.Message);
}
catch (Exception ex)
{
_logger.LogError(ex, ex.Message);
return StatusCode(500, "Unexpected Error! Please try again later.");
}
return recipientId;
}
}
@@ -0,0 +1,59 @@
using Govor.Application.Interfaces.Infrastructure.Extensions;
using Govor.Contracts.Requests;
using Govor.Core.Repositories.PushTokens;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Govor.API.Controllers;
[ApiController]
[Authorize(Roles = "Admin, User")]
[Route("api")]
public class PushTokensController : Controller
{
private readonly ICurrentUserService _currentUser;
private readonly ICurrentUserSessionService _currentSession;
private readonly IPushTokenRepository _pushTokenRepo;
public PushTokensController(
ICurrentUserService currentUser,
ICurrentUserSessionService currentSession,
IPushTokenRepository pushTokenRepository)
{
_currentUser = currentUser;
_pushTokenRepo = pushTokenRepository;
_currentSession = currentSession;
}
[HttpPost("pushes/token/register")]
public async Task<IActionResult> RegisterToken([FromBody] RegisterPushTokenRequest req)
{
try
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
if (string.IsNullOrEmpty(req.Token) || string.IsNullOrWhiteSpace(req.Platform))
return BadRequest(ModelState);
var currentId = _currentUser.GetCurrentUserId();
var currentSessionId = _currentSession.GetUserSessionId();
await _pushTokenRepo.AddOrUpdateTokenAsync(
userId: currentId,
sessionId: currentSessionId,
token: req.Token,
platform: req.Platform);
return Ok();
}
catch (ArgumentException ex)
{
return BadRequest(ex.Message);
}
catch (Exception ex)
{
return StatusCode(500, "Unexpected Error! Please try again later.");
}
}
}
@@ -2,6 +2,7 @@ using AutoMapper;
using Govor.Application.Interfaces.Infrastructure.Extensions;
using Govor.Application.Interfaces.UserSession;
using Govor.Contracts.DTOs;
using Govor.Core.Repositories.PushTokens;
using Govor.Data.Repositories.Exceptions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
+1
View File
@@ -10,6 +10,7 @@
<PackageReference Include="AutoMapper" Version="12.0.1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="FirebaseAdmin" Version="3.4.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.5" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.6" />
@@ -1,16 +1,29 @@
using Govor.Application.Interfaces;
using Govor.Application.Interfaces.PushNotifications;
using Govor.Contracts.Responses.SignalR;
using Govor.Core.Models.Messages;
using Govor.Core.Repositories.PrivateChats;
using Govor.Core.Repositories.PushTokens;
using Microsoft.AspNetCore.SignalR;
namespace Govor.API.Hubs.Infrastructure;
public class ChatNotificationService : IChatNotificationService
{
private readonly IHubContext<ChatsHub> _hubContext;
public ChatNotificationService(IHubContext<ChatsHub> hubContext)
private readonly IHubContext<ChatsHub> _hubContext;
private readonly IPrivateChatsRepository _privateChatsRepository;
private readonly IPushNotificationService _notificationService;
private readonly IProfileService _profileService;
public ChatNotificationService(
IProfileService profileService,
IHubContext<ChatsHub> hubContext,
IPushNotificationService notificationService,
IPrivateChatsRepository privateChatsRepository)
{
_hubContext = hubContext;
_profileService = profileService;
_notificationService = notificationService;
_privateChatsRepository = privateChatsRepository;
}
public async Task NotifyMessageSentAsync(UserMessageResponse message)
@@ -19,6 +32,8 @@ private readonly IHubContext<ChatsHub> _hubContext;
{
await _hubContext.Clients.Group(ChatHubConstants.GetPrivateChat(message.RecipientId))
.SendAsync(ChatHubConstants.ReceiveMessage, message);
await NotifyMessageReceivedInPrivateChatAsync(message);
}
else
{
@@ -30,6 +45,19 @@ private readonly IHubContext<ChatsHub> _hubContext;
// .SendAsync(ChatHubConstants.MessageSent, message);
}
private async Task NotifyMessageReceivedInPrivateChatAsync(UserMessageResponse message)
{
var privateChat = await _privateChatsRepository.GetByIdAsync(message.RecipientId);
var text = message.EncryptedContent.Substring(0, Math.Min(40, message.EncryptedContent.Length));
var userId = message.SenderId == privateChat.UserAId ? privateChat.UserAId : privateChat.UserBId;
var profile = await _profileService.GetUserProfileAsync(userId);
var title = profile.Username;
await _notificationService.SendToUserAsync(userId, title,text, "chat_messages");
}
public async Task NotifyMessageRemovedAsync(MessageRemovedResponse response)
{
await NotifyParticipantsAsync(
@@ -1,3 +1,4 @@
using System.Collections.Concurrent;
using Govor.Application.Interfaces;
using Microsoft.AspNetCore.SignalR;
@@ -7,11 +8,17 @@ public class ConnectionManager : IConnectionManager
{
private readonly IUserGroupsGetterService _userGroupsGetterService;
private readonly IUserPrivateChatsGetterService _userPrivateChatsGetterService;
private readonly IConnectionStore _connectionStore;
private readonly IHubContext<ChatsHub> _hubContext;
public ConnectionManager(IUserGroupsGetterService userGroupsGetterService, IUserPrivateChatsGetterService userPrivateChatsGetterService, IHubContext<ChatsHub> hubContext)
public ConnectionManager(
IUserGroupsGetterService userGroupsGetterService,
IConnectionStore connectionStore,
IUserPrivateChatsGetterService userPrivateChatsGetterService,
IHubContext<ChatsHub> hubContext)
{
_userGroupsGetterService = userGroupsGetterService;
_connectionStore = connectionStore;
_userPrivateChatsGetterService = userPrivateChatsGetterService;
_hubContext = hubContext;
}
@@ -20,7 +27,8 @@ public class ConnectionManager : IConnectionManager
{
// user
await _hubContext.Groups.AddToGroupAsync(connectionId, ChatHubConstants.GetUserGroup(userId));
_connectionStore.AddConnection(userId, connectionId);
// groups
var userGroups = await _userGroupsGetterService.GetUserGroupsAsync(userId);
foreach (var group in userGroups)
@@ -41,6 +49,7 @@ public class ConnectionManager : IConnectionManager
if (userId != Guid.Empty)
{
await _hubContext.Groups.RemoveFromGroupAsync(connectionId, ChatHubConstants.GetUserGroup(userId));
_connectionStore.RemoveConnection(userId, connectionId);
}
}
}
@@ -0,0 +1,32 @@
using System.Collections.Concurrent;
using Govor.Application.Interfaces;
namespace Govor.API.Hubs.Infrastructure;
public class ConnectionStore : IConnectionStore
{
private readonly ConcurrentDictionary<Guid, HashSet<string>> _connections = new();
public void AddConnection(Guid userId, string connectionId)
{
var conns = _connections.GetOrAdd(userId, _ => new HashSet<string>());
lock (conns) conns.Add(connectionId);
}
public void RemoveConnection(Guid userId, string connectionId)
{
if (_connections.TryGetValue(userId, out var conns))
{
lock (conns)
{
conns.Remove(connectionId);
if (!conns.Any()) _connections.TryRemove(userId, out _);
}
}
}
public IEnumerable<string> GetConnections(Guid userId)
{
return _connections.TryGetValue(userId, out var conns) ? conns.ToList() : Enumerable.Empty<string>();
}
}
@@ -0,0 +1,41 @@
using Govor.API.Hubs;
using Govor.API.Hubs.Infrastructure;
using Govor.Application.Interfaces;
using Govor.Core.Models;
using Microsoft.AspNetCore.SignalR;
namespace Govor.API.Hubs.Infrastructure;
public class PrivateChatGroupManager : IPrivateChatGroupManager
{
private readonly IConnectionStore _connectionStore;
private readonly IHubContext<ChatsHub> _hubContext;
public PrivateChatGroupManager(IConnectionStore connectionStore, IHubContext<ChatsHub> hubContext)
{
_connectionStore = connectionStore;
_hubContext = hubContext;
}
public async Task AddUsersToPrivateChatGroupAsync(PrivateChat privateChat)
{
var connectionsA = _connectionStore.GetConnections(privateChat.UserAId);
var connectionsB = _connectionStore.GetConnections(privateChat.UserBId);
foreach (var conn in connectionsA.Concat(connectionsB))
{
await _hubContext.Groups.AddToGroupAsync(conn, ChatHubConstants.GetPrivateChat(privateChat.Id));
}
}
public async Task RemoveUsersFromPrivateChatGroupAsync(PrivateChat privateChat)
{
var connectionsA = _connectionStore.GetConnections(privateChat.UserAId);
var connectionsB = _connectionStore.GetConnections(privateChat.UserBId);
foreach (var conn in connectionsA.Concat(connectionsB))
{
await _hubContext.Groups.RemoveFromGroupAsync(conn, ChatHubConstants.GetPrivateChat(privateChat.Id));
}
}
}
+9 -1
View File
@@ -1,4 +1,6 @@
using System.Text;
using FirebaseAdmin;
using Google.Apis.Auth.OAuth2;
using Govor.API.Common.Extensions;
using Govor.API.Hubs;
using Govor.Application.Services.Authentication;
@@ -15,11 +17,17 @@ var services = builder.Services;
builder.AddLogger();// Serilog
#if DEBUG
builder.Configuration.AddJsonFile("appsettings.Development.json", optional: false, reloadOnChange: true);
builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
//builder.Configuration.AddJsonFile("appsettings.Development.json", optional: false, reloadOnChange: true);
#else
builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
#endif
FirebaseApp.Create(new AppOptions()
{
Credential = GoogleCredential.FromFile("secrets/firebase-adminsdk.json")
// или FromStream(File.OpenRead("firebase-adminsdk.json"))
});
builder.Services.AddCors(options =>
{
+1 -1
View File
@@ -6,7 +6,7 @@
}
},
"ConnectionStrings": {
"GovorDbContext": "Host=localhost;Port=5432;Database=GovorDb;Username=postgres;Password=stalcker;"
"GovorDbContext": "Host=46.19.68.182;Port=5432;Database=default_db;Username=main;Password=fP6%,WzF$S?IUI;"
},
"UseMySql": false,
"AllowedHosts": "*",
+1 -1
View File
@@ -6,7 +6,7 @@
}
},
"ConnectionStrings": {
"GovorDbContext": "Host=46.19.68.182;Port=5432;Database=default_db;Username=gen_user;Password=p_mxIUCl#G6REV;"
"GovorDbContext": "Host=46.19.68.182;Port=5432;Database=default_db;Username=main;Password=fP6%,WzF$S?IUI;"
},
"UseMySql": false,
"AllowedHosts": "*",
+13
View File
@@ -0,0 +1,13 @@
{
"type": "service_account",
"project_id": "govor-ad008",
"private_key_id": "c86cedafc5fd26278d812d1eb04281415076b70b",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCxSZEVKcgorEeR\nIMobn6Fa2WDRZTwqm6A/yQ4gMJb673TvOSn7ukAZoV/IoZOR8fz84OdLzoEP0/Gu\nYtCvKdc/rsVdPVnLZZxxpk+cYbrfSstdke55E67tLjtlfF7XZn3OSQ2lRszff80z\n78DHWORaSGUb4UKrB8mnvPFZmM8lDzwmslnskQBB97nIDBxsBdUD2jw2n70kjfM5\nFgNX814PEztSqwC7nNkXl2MZxWSOP7ppjTsJrDfWGhFJ1faCS+sW3KzzI4XHMUyG\nd++Oxs7QK/jcD0/NwDMjMycRv/6WZgn2ItY+EUXza/6NMS+RikXwEiZjkfvte5IU\nkTk3Wb+nAgMBAAECggEAEAQN28QVBXue0YENURaILJ1jrjb/ivfZL7n5kTXihdPZ\nE/1tigSm4sndzdGu86nRSYUF0CbdKPFkLXVW1eQpQmMHCGfQ81uAGRJFseJwT9jB\na3gR+5UoIdwh2Ia2SK5FIjweQ/aT7oLspkr79uZUZsWQgMbGT44aDRG4GfiAih/i\nCye02cBUt6LwYTSyn4toX/Hsnoew60qLsyo6UiyE18eOrrFaK0Rcc8ia8jJ7j4K/\niKywX+PiK9z9dp95G/bpspfPkTh1XE1LEL8ArglnOLSJtbewmOrGK/PODo6EaVIU\nH4uv/0ThIkjDN5PvvZZkBXLdZ/mLyAPa2Xh5hvih0QKBgQDqkSSTAJNQG3UgH3cq\n8So4XuFbsWB4zCDqpBz+WitmKJUzYNhXyTZ3Sx7JbculNFESaX2lmeHTW/7vwa/Z\nZrt6npSlR6Ln3AitCyZRE46tBlaN3kBJvTolRLZvBS+Rq/bYedNyN8Ls18yoQcUj\nsGn1Jetjl3M8QQl7AmnOb2aclwKBgQDBfJJ34ba2LwsQTTwdbCeyCGOAyj59qQaq\nB4oGKb24SDJMbOGa7p17PSwvnRUmdNXmT9QKawBf3H2d3MWBpHW1024Ycg64ZD6Y\nLCpye7wLeNs3OPyFDfpi7uXKbpBA/cjU1uI1eqXC197VmU/BAk6TxlRijY3zK1kU\n9bA0pb2HcQKBgHBV1mg4TFR+8dbSeuWr3YZlmhOpnQP87n6w3dnKISKpqNqUNMfF\n6zmyViotVOvnZDQnJS9bxNTOKAd2gNri4kJVE+cbqZ7Ut6r3vf/yF9AxSt5iY/Ns\nlh4nDB+bIi7nZi5CGcuHfOikaLTj2p++6t+mq1Zkv6FJnFq2yyk8HK49AoGBAJ5N\nADyq4+TsWj3tItjjqxqCuH4feb8vsi8cWfWu4vTJxLU4g+BRh3DT3Lnb5/j9sB9t\ngos/fh/v8qpcfQ8TcebgY/wGHTsJcRjpUZU23OP57kihDCEEDa4xzLmxeb5ipJRW\ndt7QSJxAJ6VUeKbt70ICCvpS3CdueMSoOpDoZUdRAoGBALY0kemgKgLxHDNXZEjI\n3/ndIdea5aI2q2UpZHEdfLcCE1Fb/bVjtx9ZTLsxxm25L7sdqHqxivMxmEMhgxc4\nXzvUtA22i4FIF2g6M/31gWeFtHvJWczeit/FMsw271HxPflVuuf4cxaIcYkccJTd\nQhbyfbGJAhVBR02CeDXM3lOy\n-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-fbsvc@govor-ad008.iam.gserviceaccount.com",
"client_id": "115697205969744490044",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-fbsvc%40govor-ad008.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}