diff --git a/backend/src/Squidex.Domain.Apps.Entities/History/NotifoService.cs b/backend/src/Squidex.Domain.Apps.Entities/History/NotifoService.cs index 9c2ba4313..b883f1961 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/History/NotifoService.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/History/NotifoService.cs @@ -131,6 +131,13 @@ namespace Squidex.Domain.Apps.Entities.History await userResolver.SetClaimAsync(user.Id, SquidexClaimTypes.NotifoKey, response.First().ApiKey, true); } + catch (NotifoException ex) + { + log.LogError(ex, w => w + .WriteProperty("action", "RegisterToNotifo") + .WriteProperty("status", "Failed") + .WriteProperty("details", ex.ToString())); + } catch (Exception ex) { log.LogError(ex, w => w @@ -148,140 +155,168 @@ namespace Squidex.Domain.Apps.Entities.History return; } - var now = clock.GetCurrentInstant(); - - var publishedEvents = events - .Where(x => x.AppEvent.Headers.Restored() == false) - .Where(x => IsTooOld(x.AppEvent.Headers, now) == false) - .Where(x => IsComment(x.AppEvent.Payload) || x.HistoryEvent != null); - - foreach (var batch in publishedEvents.Batch(50)) + try { - var requests = new List(); + var now = clock.GetCurrentInstant(); + + var publishedEvents = events + .Where(x => x.AppEvent.Headers.Restored() == false) + .Where(x => IsTooOld(x.AppEvent.Headers, now) == false) + .Where(x => IsComment(x.AppEvent.Payload) || x.HistoryEvent != null); - foreach (var @event in batch) + foreach (var batch in publishedEvents.Batch(50)) { - var payload = @event.AppEvent.Payload; + var requests = new List(); - if (payload is CommentCreated comment && IsComment(payload)) + foreach (var @event in batch) { - foreach (var userId in comment.Mentions!) - { - var publishRequest = new PublishDto - { - Topic = $"users/{userId}" - }; + var payload = @event.AppEvent.Payload; - publishRequest.Properties["SquidexApp"] = comment.AppId.Name; + if (payload is CommentCreated comment && IsComment(payload)) + { + AddMentions(requests, comment); + } + else if (@event.HistoryEvent != null) + { + AddHistoryEvent(requests, @event.HistoryEvent, payload); + } + } - publishRequest.Preformatted = new NotificationFormattingDto - { - Subject = - { - ["en"] = comment.Text - } - }; + var request = new PublishManyDto + { + Requests = requests + }; - if (comment.Url?.IsAbsoluteUri == true) - { - publishRequest.Preformatted.LinkUrl["en"] = comment.Url.ToString(); - } + await client.Events.PostEventsAsync(options.AppId, request); + } - SetUser(comment, publishRequest); + foreach (var @event in events) + { + switch (@event.AppEvent.Payload) + { + case AppContributorAssigned contributorAssigned: + await AssignContributorAsync(client, contributorAssigned); + break; - requests.Add(publishRequest); - } + case AppContributorRemoved contributorRemoved: + await RemoveContributorAsync(client, contributorRemoved); + break; } - else if (@event.HistoryEvent != null) - { - var historyEvent = @event.HistoryEvent; + } + } + catch (NotifoException ex) + { + log.LogError(ex, w => w + .WriteProperty("action", "RegisterToNotifo") + .WriteProperty("status", "Failed") + .WriteProperty("details", ex.ToString())); + } + catch (Exception ex) + { + log.LogError(ex, w => w + .WriteProperty("action", "RegisterToNotifo") + .WriteProperty("status", "Failed")); + } + } - var publishRequest = new PublishDto - { - Properties = new EventProperties() - }; + private async Task AssignContributorAsync(INotifoClient actualClient, AppContributorAssigned contributorAssigned) + { + var userId = contributorAssigned.ContributorId; - foreach (var (key, value) in historyEvent.Parameters) - { - publishRequest.Properties.Add(key, value); - } + var user = await userResolver.FindByIdAsync(userId); - publishRequest.Properties["SquidexApp"] = payload.AppId.Name; + if (user != null) + { + await UpsertUserAsync(user); + } - if (payload is ContentEvent c && !(payload is ContentDeleted)) - { - var url = urlGenerator.ContentUI(c.AppId, c.SchemaId, c.ContentId); + try + { + var request = new AddAllowedTopicDto + { + Prefix = GetAppPrefix(contributorAssigned) + }; - publishRequest.Properties["SquidexUrl"] = url; - } + await actualClient.Users.PostAllowedTopicAsync(options.AppId, userId, request); + } + catch (NotifoException ex) when (ex.StatusCode != 404) + { + throw; + } + } - publishRequest.TemplateCode = @event.HistoryEvent.EventType; + private async Task RemoveContributorAsync(INotifoClient actualClient, AppContributorRemoved contributorRemoved) + { + var userId = contributorRemoved.ContributorId; - SetUser(payload, publishRequest); - SetTopic(payload, publishRequest, historyEvent); + try + { + var prefix = GetAppPrefix(contributorRemoved); - requests.Add(publishRequest); - } - } + await actualClient.Users.DeleteAllowedTopicAsync(options.ApiKey, userId, prefix); + } + catch (NotifoException ex) when (ex.StatusCode != 404) + { + throw; + } + } - var request = new PublishManyDto - { - Requests = requests - }; + private void AddHistoryEvent(List requests, HistoryEvent historyEvent, AppEvent payload) + { + var publishRequest = new PublishDto + { + Properties = new EventProperties() + }; - await client.Events.PostEventsAsync(options.AppId, request); + foreach (var (key, value) in historyEvent.Parameters) + { + publishRequest.Properties.Add(key, value); } - foreach (var @event in events) - { - switch (@event.AppEvent.Payload) - { - case AppContributorAssigned contributorAssigned: - { - var userId = contributorAssigned.ContributorId; + publishRequest.Properties["SquidexApp"] = payload.AppId.Name; - var user = await userResolver.FindByIdAsync(userId); + if (payload is ContentEvent c && !(payload is ContentDeleted)) + { + var url = urlGenerator.ContentUI(c.AppId, c.SchemaId, c.ContentId); - if (user != null) - { - await UpsertUserAsync(user); - } + publishRequest.Properties["SquidexUrl"] = url; + } - try - { - var request = new AddAllowedTopicDto - { - Prefix = GetAppPrefix(contributorAssigned) - }; + publishRequest.TemplateCode = historyEvent.EventType; - await client.Users.PostAllowedTopicAsync(options.AppId, userId, request); - } - catch (NotifoException ex) when (ex.StatusCode == 404) - { - break; - } + SetUser(payload, publishRequest); + SetTopic(payload, publishRequest, historyEvent); - break; - } + requests.Add(publishRequest); + } - case AppContributorRemoved contributorRemoved: - { - var userId = contributorRemoved.ContributorId; + private static void AddMentions(List requests, CommentCreated comment) + { + foreach (var userId in comment.Mentions!) + { + var publishRequest = new PublishDto + { + Topic = $"users/{userId}" + }; - try - { - var prefix = GetAppPrefix(contributorRemoved); + publishRequest.Properties["SquidexApp"] = comment.AppId.Name; - await client.Users.DeleteAllowedTopicAsync(options.ApiKey, userId, prefix); - } - catch (NotifoException ex) when (ex.StatusCode == 404) - { - break; - } + publishRequest.Preformatted = new NotificationFormattingDto + { + Subject = + { + ["en"] = comment.Text + } + }; - break; - } + if (comment.Url?.IsAbsoluteUri == true) + { + publishRequest.Preformatted.LinkUrl["en"] = comment.Url.ToString(); } + + SetUser(comment, publishRequest); + + requests.Add(publishRequest); } } diff --git a/backend/src/Squidex/appsettings.json b/backend/src/Squidex/appsettings.json index d345219ec..8063314d2 100644 --- a/backend/src/Squidex/appsettings.json +++ b/backend/src/Squidex/appsettings.json @@ -247,6 +247,24 @@ } }, + /* + * Configure notifo if you want to have support for custom notifications. + */ + "notifo": { + /* + * The id of the app in notifo. + */ + "appId": "", + /* + * The API key for your app in notifo. + */ + "apiKey": "", + /* + * The API URL. + */ + "apiUrl": "https://app.notifo.io" + }, + "robots": { /* * The text for the robots.txt file