using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading.Tasks; using Asp.Versioning; using AutoMapper; using gehGassi.Core.Interfaces; using gehGassi.Core.Services; using gehGassi.Domain.Common; using gehGassi.Domain.Dogs; using gehGassi.Domain.Messages; using gehGassi.Domain.Pushnotifications; using gehGassi.Dto; using gehGassi.Dto.Common; using gehGassi.Dto.Favourites; using gehGassi.Dto.Messages; using gehGassi.Web.Auth; using gehGassi.Web.Hubs; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using LocalizationOptions = gehGassi.Web.Helper.LocalizationOptions; namespace gehGassi.Web.Controllers.Api { /// /// Controller der Zugriff auf Nachrichten via API ermöglicht /// [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] [ApiController] [ApiVersion(1)] [Route("api/messages")] [Route("api/v{v:apiVersion}/messages")] public class ApiMessagesController : ApiBaseController { private readonly ILogger _logger; private readonly IMessageService _messageService; private readonly IAppHubSender _appHubSender; private readonly ISystemMessageService _systemMessageService; private readonly IPushNotificationService _pushNotificationService; private readonly IDeviceService _deviceService; private readonly IStringLocalizer _localizer; private readonly IUserOnlineService _userOnlineService; /// /// Erstellt eine Instanz /// /// Instanz eines IMapper /// Instanz eines ILogger /// Instanz von LocalizationOptions /// Instanz eines IDogOwnerService /// Instanz eines IMessageService /// Instanz eines IAppHubSender /// /// Instanz eines IPushNotificationService /// Instanz eines IDeviceService /// Instanz eines IStringLocalizer /// Instanz eines IUserOnlineService public ApiMessagesController(IMapper mapper, ILogger logger, IOptions localizationOptions, IAppUserService appUserService, IMessageService messageService, IAppHubSender appHubSender, ISystemMessageService systemMessageService, IPushNotificationService pushNotificationService, IDeviceService deviceService, IStringLocalizer localizer, IUserOnlineService userOnlineService) : base(mapper, localizationOptions, appUserService) { _logger = logger; _messageService = messageService; _appHubSender = appHubSender; _systemMessageService = systemMessageService; _pushNotificationService = pushNotificationService; _deviceService = deviceService; _localizer = localizer; _userOnlineService = userOnlineService; } /// /// Anlegen einer Konversation /// /// CreateConversationDto /// HTTP 200 OK, Felher sonst [HttpPost] [Route("CreateConversation")] public async Task CreateConversation(CreateConversationDto model) { var clientOffset = GetClientDateOffset(); var result = new CreateResponseDto { Status = CreateStatusDto.Error, Value = null }; if (User?.Identity != null && User.Identity.IsAuthenticated) { if (ModelState.IsValid) { if (model.SenderId != model.ReceiverId) { //Wenn Sender und Emfpägner nicht gleich sind! var otherId = string.Empty; //Prüfen ob es die Konversation schon gibt... var conversation = await _messageService.GetConversationAsync(model.SenderId, model.ReceiverId); if (conversation == null) { conversation = await _messageService.AddConversationAsync(model.Id, model.SenderId, model.ReceiverId, User.Identity.Name); result.Status = CreateStatusDto.Success; result.Value = Mapper.Map(conversation); otherId = model.ReceiverId; } else { result.Status = CreateStatusDto.Exists; result.Value = Mapper.Map(conversation); if (model.SenderId == conversation.SenderId) { otherId = model.ReceiverId; } else { otherId = model.SenderId; } } var baseAddress = GetBaseAddress(); var appUser = await AppUserService.GetAsync(otherId); result.Value.ReceipientType = (AppUserTypeDto)appUser.Type; result.Value.Recipient = appUser.Id; result.Value.RecipientName = $"{appUser.FirstName} {appUser.LastName}"; result.Value.RecipientShort = GetInitials(appUser.FirstName, appUser.LastName); result.Value.RecipientPhoto = !string.IsNullOrWhiteSpace(appUser.Photo) ? $"{baseAddress}/file/documents/thumbnails/{100}/{appUser.Photo}" : ""; return Ok(result); } } } return BadRequest(CommunicationErrors.Common_Model_Invalid); } /// /// Abfragen der Konversationen eines Users /// /// Id des Senders /// Letztes Update /// Liste der Konversationen [HttpGet] [Route("GetConversations")] public async Task GetConversations(string senderId, DateTimeOffset? lastUpdate) { var clientOffset = GetClientDateOffset(); if (User?.Identity != null && User.Identity.IsAuthenticated) { var conversations = await _messageService.GetConversationsAsync(senderId, lastUpdate); var conversationDtoList = Mapper.Map>(conversations); var baseAddress = GetBaseAddress(); foreach (var item in conversationDtoList) { var otherId = string.Empty; var conversation = conversations.First(c => c.Id == item.Id); if (conversation.SenderId == senderId) { otherId = conversation.ReceiverId; } else { otherId = conversation.SenderId; } var appUser = await AppUserService.GetAsync(otherId); item.ReceipientType = (AppUserTypeDto)appUser.Type; item.Recipient = appUser.Id; item.RecipientName = $"{appUser.FirstName} {appUser.LastName}"; item.RecipientShort = GetInitials(appUser.FirstName, appUser.LastName); item.RecipientPhoto = !string.IsNullOrWhiteSpace(appUser.Photo) ? $"{baseAddress}/file/documents/thumbnails/{100}/{appUser.Photo}" : ""; } return Ok(conversationDtoList); } return NotFound(CommunicationErrors.Common_NotFound); } /// /// Abfragen der Konversationen eines Users /// /// Id des Senders /// Filterwert der in Namen gesucht wird /// Liste der Konversationen [HttpGet] [Route("GetConversationsEx")] public async Task GetConversationsEx(string senderId, string filter) { var clientOffset = GetClientDateOffset(); filter ??= string.Empty; if (User?.Identity != null && User.Identity.IsAuthenticated) { var conversations = await _messageService.GetConversationsWithNamesAsync(senderId); var conversationList = new List(); foreach (var conversation in conversations) { if (conversation.SenderId == senderId) { if (!conversation.ReceiverBlocked && !conversation.ReceiverLocked && conversation.ReceiverName.Contains(filter)) conversationList.Add(conversation); } else { if (!conversation.SenderBlocked && !conversation.SenderLocked && conversation.SenderName.Contains(filter)) conversationList.Add(conversation); } } var conversationDtoList = Mapper.Map>(conversationList); var baseAddress = GetBaseAddress(); foreach (var item in conversationDtoList) { var conversation = conversationList.First(c => c.Id == item.Id); if (conversation.SenderId == senderId) { item.ReceipientType = (AppUserTypeDto)conversation.ReceiverType; item.Recipient = conversation.ReceiverId; item.RecipientName = conversation.ReceiverName; item.RecipientShort = GetInitials(conversation.ReceiverFirstName, conversation.ReceiverLastName); item.RecipientPhoto = !string.IsNullOrWhiteSpace(conversation.ReceiverPhoto) ? $"{baseAddress}/file/documents/thumbnails/{100}/{conversation.ReceiverPhoto}" : ""; } else { item.ReceipientType = (AppUserTypeDto)conversation.SenderType; item.Recipient = conversation.SenderId; item.RecipientName = conversation.SenderName; item.RecipientShort = GetInitials(conversation.SenderFirstName, conversation.SenderLastName); item.RecipientPhoto = !string.IsNullOrWhiteSpace(conversation.SenderPhoto) ? $"{baseAddress}/file/documents/thumbnails/{100}/{conversation.SenderPhoto}" : ""; } } return Ok(conversationDtoList); } return NotFound(CommunicationErrors.Common_NotFound); } /// /// Abfragen der Konversationen eines Benutzers mit einem Emüfänger /// /// Id des Senders /// Id des Empfängers nach dem gesucht wird /// Konversationen oder NotFound wenn nicht gefunden [HttpGet] [Route("GetConversationByReceipient")] public async Task GetConversationByReceipient(string senderId, string receipientId) { var clientOffset = GetClientDateOffset(); if (User?.Identity != null && User.Identity.IsAuthenticated) { var conversation = await _messageService.GetConversationAsync(senderId, receipientId); if (conversation != null) { var baseAddress = GetBaseAddress(); var otherId = string.Empty; if (conversation.SenderId == senderId) { otherId = conversation.ReceiverId; } else { otherId = conversation.SenderId; } var item = Mapper.Map(conversation); var appUser = await AppUserService.GetAsync(otherId); item.ReceipientType = (AppUserTypeDto)appUser.Type; item.Recipient = appUser.Id; item.RecipientName = $"{appUser.FirstName} {appUser.LastName}"; item.RecipientShort = GetInitials(appUser.FirstName, appUser.LastName); item.RecipientPhoto = !string.IsNullOrWhiteSpace(appUser.Photo) ? $"{baseAddress}/file/documents/thumbnails/{100}/{appUser.Photo}" : ""; return Ok(item); } } return NotFound(CommunicationErrors.Common_NotFound); } /// /// Abfragen der bisher noch nicht abgerufenen Nachrichten für einen Benutzer /// /// Id des Senders - gilt dann als Empfänger /// Letztes Update /// Liste Nachrichten [HttpGet] [Route("GetMessages")] public async Task GetMessages(string senderId, DateTimeOffset? lastUpdate) { var clientOffset = GetClientDateOffset(); if (User?.Identity != null && User.Identity.IsAuthenticated) { var messages = await _messageService.GetMessagesAsync(senderId, lastUpdate); var messagesDtoList = Mapper.Map>(messages); return Ok(messagesDtoList); } return NotFound(CommunicationErrors.Common_NotFound); } /// /// Abfragen der bisher noch nicht abgerufenen Nachrichten für einen Benutzer in einer Konversation /// /// Id des Senders - gilt dann als Empfänger /// Id der Konversation /// Letztes Update /// Liste Nachrichten [HttpGet] [Route("GetMessagesByConversation")] public async Task GetMessagesByConversation(string senderId, string conversationId, DateTimeOffset? lastUpdate) { var clientOffset = GetClientDateOffset(); if (User?.Identity != null && User.Identity.IsAuthenticated) { var messages = await _messageService.GetMessagesAsync(senderId, conversationId, lastUpdate); var messagesDtoList = Mapper.Map>(messages); return Ok(messagesDtoList); } return NotFound(CommunicationErrors.Common_NotFound); } /// /// Bestätigen des erfolgreichen Erhalts von Nachrichten /// /// MessageConfirmationDto /// HTTP 200 OK, Felher sonst [HttpPost] [Route("ConfirmMessages")] public async Task ConfirmMessages(MessageConfirmationDto model) { var clientOffset = GetClientDateOffset(); if (User?.Identity != null && User.Identity.IsAuthenticated) { if (ModelState.IsValid) { await _messageService.ConfirmMessagesAsync(model.SenderId, model.LastUpdate.Value, User.Identity.Name); return Ok(); } } return BadRequest(CommunicationErrors.Common_Model_Invalid); } /// /// Bestätigen des erfolgreichen Erhalts von Nachrichten für eine Konversation /// /// MessageConfirmationDto /// HTTP 200 OK, Felher sonst [HttpPost] [Route("ConfirmMessagesByConversation")] public async Task ConfirmMessagesByConversation(MessageConfirmationDto model) { var clientOffset = GetClientDateOffset(); if (User?.Identity != null && User.Identity.IsAuthenticated) { if (ModelState.IsValid) { await _messageService.ConfirmMessagesAsync(model.SenderId, model.ConversationId, model.LastUpdate.Value, User.Identity.Name); return Ok(); } } return BadRequest(CommunicationErrors.Common_Model_Invalid); } /// /// Hinzufügen einer Nachricht zu einer Konversation /// /// MessageDto /// HTTP 200 OK, Felher sonst [HttpPost] [Route("AddMessage")] public async Task AddMessage(MessageDto model) { var clientOffset = GetClientDateOffset(); var result = new CreateResponseDto { Status = CreateStatusDto.Error, Value = null }; if (User?.Identity != null && User.Identity.IsAuthenticated) { if (ModelState.IsValid) { var appUserId = User.AppUserId(); var isLocked = await AppUserService.IsLockedAsync(appUserId); var isBlocked = await AppUserService.IsBlockedAsync(model.ReceiverId, model.SenderId); var message = Mapper.Map(model); var existingMessage = await _messageService.GetAsync(message.Id); if (existingMessage == null && !isLocked && !isBlocked) { message = await _messageService.AddMessageAsync(message, User.Identity.Name); var conversation = await _messageService.GetConversationAsync(message.SenderId, message.ReceiverId); if (conversation != null) { conversation.MessageCount += 1; conversation.LastMessage = DateTimeOffset.UtcNow; await _messageService.CommitAsync(User.Identity.Name); } result.Status = CreateStatusDto.Success; result.Value = model; await _appHubSender.MessageAddedAsync(model.ReceiverId); var isOnline = await _userOnlineService.IsAppUserOnlineAsync(model.ReceiverId); if (!isOnline) { //Pushnotification nur senden, wenn es sich um eine offene Nachricht handelt var openMessageCount = await _messageService.CountOpenMessagesAsync(model.ReceiverId, model.ConversationId); if (openMessageCount == 1) { var success = await _pushNotificationService.SendNewMessageAsync(model.ReceiverId, model.ConversationId); } } } return Ok(result); } } return BadRequest(CommunicationErrors.Common_Model_Invalid); } #region Systemmessages /// /// Abfrage der Systemnachrichten eines AppUsers /// /// Id des AppUsers /// Letztes Update oder null, wenn noch keines /// Liste der Favoriten [HttpGet] [Route("GetSystemMessagesForSync")] public async Task GetSystemMessagesForSync(string appUserId, DateTimeOffset? lastUpdate) { var clientOffset = GetClientDateOffset(); if (User?.Identity != null && User.Identity.IsAuthenticated) { var systemMessages = await _systemMessageService.GetForSyncAppAsync(appUserId, lastUpdate); var dtoList = Mapper.Map>(systemMessages); return Ok(dtoList); } return NotFound(CommunicationErrors.Common_NotFound); } /// /// Bestätigen des erfolgreichen Erhalts von Systemnachrichten /// /// SystemMessageConfirmationDto /// HTTP 200 OK, Felher sonst [HttpPost] [Route("ConfirmSystemMessages")] public async Task ConfirmSystemMessages(SystemMessageConfirmationDto model) { var clientOffset = GetClientDateOffset(); if (User?.Identity != null && User.Identity.IsAuthenticated) { if (ModelState.IsValid) { await _systemMessageService.ConfirmAsync(model.SenderId, model.LastUpdate.Value, User.Identity.Name); return Ok(); } } return BadRequest(CommunicationErrors.Common_Model_Invalid); } /// /// Löschen von Systemnachrichten für einen AppUser wenn die Details in der App schon verfügbar sind /// /// SystemMessageRemoveDto /// HTTP 200 OK, Felher sonst [HttpPost] [Route("RemoveSystemMessages")] public async Task RemoveSystemMessages(SystemMessageRemoveDto model) { var clientOffset = GetClientDateOffset(); if (User?.Identity != null && User.Identity.IsAuthenticated) { if (ModelState.IsValid) { await _systemMessageService.RemoveAsync(model.AppUserId, (AppUserType)model.AppUserType, model.Key, model.Table, model.Created, User.Identity.Name); return Ok(); } } return BadRequest(CommunicationErrors.Common_Model_Invalid); } #endregion #region Helper /// /// Hilfsmethode zum erstellen der Initialen aus Vor- und Nachname /// /// Vorname /// Nachname /// Initialen private string GetInitials(string firstName, string lastName) { if (!string.IsNullOrWhiteSpace(firstName) && !string.IsNullOrWhiteSpace(lastName)) return $"{firstName.Substring(0, 1)}{lastName.Substring(0, 1)}"; else if (!string.IsNullOrWhiteSpace(firstName) && firstName.Length >= 2 && string.IsNullOrWhiteSpace(lastName)) return $"{firstName.Substring(0, 2)}"; else if (string.IsNullOrWhiteSpace(firstName) && string.IsNullOrWhiteSpace(lastName) && lastName.Length >= 2) return $"{lastName.Substring(0, 2)}"; else return "??"; } #endregion } }