summaryrefslogtreecommitdiff
path: root/internal/processing
diff options
context:
space:
mode:
authorLibravatar Vyr Cossont <VyrCossont@users.noreply.github.com>2025-01-23 16:47:30 -0800
committerLibravatar GitHub <noreply@github.com>2025-01-23 16:47:30 -0800
commit5b765d734ee70f0a8a0790444d60969a727567f8 (patch)
treef76e05a6e5b22df17160be595c40e964bdbe5f22 /internal/processing
parent[feature] Serve bot accounts over AP as Service instead of Person (#3672) (diff)
downloadgotosocial-5b765d734ee70f0a8a0790444d60969a727567f8.tar.xz
[feature] Push notifications (#3587)
* Update push subscription API model to be Mastodon 4.0 compatible * Add webpush-go dependency # Conflicts: # go.sum * Single-row table for storing instance's VAPID key pair * Generate VAPID key pair during startup * Add VAPID public key to instance info API * Return VAPID public key when registering an app * Store Web Push subscriptions in DB * Add Web Push sender (similar to email sender) * Add no-op push senders to most processor tests * Test Web Push notifications from workers * Delete Web Push subscriptions when account is deleted * Implement push subscription API * Linter fixes * Update Swagger * Fix enum to int migration * Fix GetVAPIDKeyPair * Create web push subscriptions table with indexes * Log Web Push server error messages * Send instance URL as Web Push JWT subject * Accept any 2xx code as a success * Fix malformed VAPID sub claim * Use packed notification flags * Remove unused date columns * Add notification type for update notifications Not used yet * Make GetVAPIDKeyPair idempotent and remove PutVAPIDKeyPair * Post-rebase fixes * go mod tidy * Special-case 400 errors other than 408/429 Most client errors should remove the subscription. * Improve titles, trim body to reasonable length * Disallow cleartext HTTP for Web Push servers * Fix lint * Remove redundant index on unique column Also removes redundant unique and notnull tags on ID column since these are implied by pk * Make realsender.go more readable * Use Tobi's style for wrapping errors * Restore treating all 5xx codes as temporary problems * Always load target account settings * Stub `policy` and `standard` * webpush.Sender: take type converter as ctor param * Move webpush.MockSender and noopSender into testrig
Diffstat (limited to 'internal/processing')
-rw-r--r--internal/processing/account/delete.go6
-rw-r--r--internal/processing/admin/admin_test.go1
-rw-r--r--internal/processing/processor.go10
-rw-r--r--internal/processing/processor_test.go1
-rw-r--r--internal/processing/push/create.go65
-rw-r--r--internal/processing/push/delete.go39
-rw-r--r--internal/processing/push/get.go47
-rw-r--r--internal/processing/push/push.go85
-rw-r--r--internal/processing/push/update.go63
-rw-r--r--internal/processing/timeline/notification.go2
-rw-r--r--internal/processing/workers/fromclientapi_test.go82
-rw-r--r--internal/processing/workers/fromfediapi_test.go4
-rw-r--r--internal/processing/workers/surface.go2
-rw-r--r--internal/processing/workers/surfacenotify.go9
-rw-r--r--internal/processing/workers/surfacenotify_test.go1
-rw-r--r--internal/processing/workers/workers.go3
16 files changed, 414 insertions, 6 deletions
diff --git a/internal/processing/account/delete.go b/internal/processing/account/delete.go
index c8d1ba5f9..2618fdfc5 100644
--- a/internal/processing/account/delete.go
+++ b/internal/processing/account/delete.go
@@ -96,7 +96,7 @@ func (p *Processor) Delete(
}
// deleteUserAndTokensForAccount deletes the gtsmodel.User and
-// any OAuth tokens and applications for the given account.
+// any OAuth tokens, applications, and Web Push subscriptions for the given account.
//
// Callers to this function should already have checked that
// this is a local account, or else it won't have a user associated
@@ -129,6 +129,10 @@ func (p *Processor) deleteUserAndTokensForAccount(ctx context.Context, account *
}
}
+ if err := p.state.DB.DeleteWebPushSubscriptionsByAccountID(ctx, account.ID); err != nil {
+ return gtserror.Newf("db error deleting Web Push subscriptions: %w", err)
+ }
+
columns, err := stubbifyUser(user)
if err != nil {
return gtserror.Newf("error stubbifying user: %w", err)
diff --git a/internal/processing/admin/admin_test.go b/internal/processing/admin/admin_test.go
index f0839f2f6..ad9d9b2ae 100644
--- a/internal/processing/admin/admin_test.go
+++ b/internal/processing/admin/admin_test.go
@@ -119,6 +119,7 @@ func (suite *AdminStandardTestSuite) SetupTest() {
suite.mediaManager,
&suite.state,
suite.emailSender,
+ testrig.NewNoopWebPushSender(),
visibility.NewFilter(&suite.state),
interaction.NewFilter(&suite.state),
)
diff --git a/internal/processing/processor.go b/internal/processing/processor.go
index 8dabfba96..0bba23089 100644
--- a/internal/processing/processor.go
+++ b/internal/processing/processor.go
@@ -39,6 +39,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/processing/markers"
"github.com/superseriousbusiness/gotosocial/internal/processing/media"
"github.com/superseriousbusiness/gotosocial/internal/processing/polls"
+ "github.com/superseriousbusiness/gotosocial/internal/processing/push"
"github.com/superseriousbusiness/gotosocial/internal/processing/report"
"github.com/superseriousbusiness/gotosocial/internal/processing/search"
"github.com/superseriousbusiness/gotosocial/internal/processing/status"
@@ -51,6 +52,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/subscriptions"
"github.com/superseriousbusiness/gotosocial/internal/text"
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
+ "github.com/superseriousbusiness/gotosocial/internal/webpush"
)
// Processor groups together processing functions and
@@ -88,6 +90,7 @@ type Processor struct {
markers markers.Processor
media media.Processor
polls polls.Processor
+ push push.Processor
report report.Processor
search search.Processor
status status.Processor
@@ -146,6 +149,10 @@ func (p *Processor) Polls() *polls.Processor {
return &p.polls
}
+func (p *Processor) Push() *push.Processor {
+ return &p.push
+}
+
func (p *Processor) Report() *report.Processor {
return &p.report
}
@@ -188,6 +195,7 @@ func NewProcessor(
mediaManager *mm.Manager,
state *state.State,
emailSender email.Sender,
+ webPushSender webpush.Sender,
visFilter *visibility.Filter,
intFilter *interaction.Filter,
) *Processor {
@@ -221,6 +229,7 @@ func NewProcessor(
processor.list = list.New(state, converter)
processor.markers = markers.New(state, converter)
processor.polls = polls.New(&common, state, converter)
+ processor.push = push.New(state, converter)
processor.report = report.New(state, converter)
processor.tags = tags.New(state, converter)
processor.timeline = timeline.New(state, converter, visFilter)
@@ -241,6 +250,7 @@ func NewProcessor(
converter,
visFilter,
emailSender,
+ webPushSender,
&processor.account,
&processor.media,
&processor.stream,
diff --git a/internal/processing/processor_test.go b/internal/processing/processor_test.go
index f152f3fad..84ab9ef48 100644
--- a/internal/processing/processor_test.go
+++ b/internal/processing/processor_test.go
@@ -135,6 +135,7 @@ func (suite *ProcessingStandardTestSuite) SetupTest() {
suite.mediaManager,
&suite.state,
suite.emailSender,
+ testrig.NewNoopWebPushSender(),
visibility.NewFilter(&suite.state),
interaction.NewFilter(&suite.state),
)
diff --git a/internal/processing/push/create.go b/internal/processing/push/create.go
new file mode 100644
index 000000000..42a67dc19
--- /dev/null
+++ b/internal/processing/push/create.go
@@ -0,0 +1,65 @@
+// GoToSocial
+// Copyright (C) GoToSocial Authors admin@gotosocial.org
+// SPDX-License-Identifier: AGPL-3.0-or-later
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package push
+
+import (
+ "context"
+
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ "github.com/superseriousbusiness/gotosocial/internal/gtserror"
+ "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
+ "github.com/superseriousbusiness/gotosocial/internal/id"
+)
+
+// CreateOrReplace creates a Web Push subscription for the given access token,
+// or entirely replaces the previously existing subscription for that token.
+func (p *Processor) CreateOrReplace(
+ ctx context.Context,
+ accountID string,
+ accessToken string,
+ request *apimodel.WebPushSubscriptionCreateRequest,
+) (*apimodel.WebPushSubscription, gtserror.WithCode) {
+ tokenID, errWithCode := p.getTokenID(ctx, accessToken)
+ if errWithCode != nil {
+ return nil, errWithCode
+ }
+
+ // Clear any previous subscription.
+ if err := p.state.DB.DeleteWebPushSubscriptionByTokenID(ctx, tokenID); err != nil {
+ err := gtserror.Newf("couldn't delete Web Push subscription for token ID %s: %w", tokenID, err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ // Insert a new one.
+ subscription := &gtsmodel.WebPushSubscription{
+ ID: id.NewULID(),
+ AccountID: accountID,
+ TokenID: tokenID,
+ Endpoint: request.Subscription.Endpoint,
+ Auth: request.Subscription.Keys.Auth,
+ P256dh: request.Subscription.Keys.P256dh,
+ NotificationFlags: alertsToNotificationFlags(request.Data.Alerts),
+ }
+
+ if err := p.state.DB.PutWebPushSubscription(ctx, subscription); err != nil {
+ err := gtserror.Newf("couldn't create Web Push subscription for token ID %s: %w", tokenID, err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ return p.apiSubscription(ctx, subscription)
+}
diff --git a/internal/processing/push/delete.go b/internal/processing/push/delete.go
new file mode 100644
index 000000000..6f5c61444
--- /dev/null
+++ b/internal/processing/push/delete.go
@@ -0,0 +1,39 @@
+// GoToSocial
+// Copyright (C) GoToSocial Authors admin@gotosocial.org
+// SPDX-License-Identifier: AGPL-3.0-or-later
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package push
+
+import (
+ "context"
+
+ "github.com/superseriousbusiness/gotosocial/internal/gtserror"
+)
+
+// Delete deletes the Web Push subscription for the given access token, if there is one.
+func (p *Processor) Delete(ctx context.Context, accessToken string) gtserror.WithCode {
+ tokenID, errWithCode := p.getTokenID(ctx, accessToken)
+ if errWithCode != nil {
+ return errWithCode
+ }
+
+ if err := p.state.DB.DeleteWebPushSubscriptionByTokenID(ctx, tokenID); err != nil {
+ err := gtserror.Newf("couldn't delete Web Push subscription for token ID %s: %w", tokenID, err)
+ return gtserror.NewErrorInternalError(err)
+ }
+
+ return nil
+}
diff --git a/internal/processing/push/get.go b/internal/processing/push/get.go
new file mode 100644
index 000000000..542f08862
--- /dev/null
+++ b/internal/processing/push/get.go
@@ -0,0 +1,47 @@
+// GoToSocial
+// Copyright (C) GoToSocial Authors admin@gotosocial.org
+// SPDX-License-Identifier: AGPL-3.0-or-later
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package push
+
+import (
+ "context"
+ "errors"
+
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ "github.com/superseriousbusiness/gotosocial/internal/db"
+ "github.com/superseriousbusiness/gotosocial/internal/gtserror"
+)
+
+// Get returns the Web Push subscription for the given access token.
+func (p *Processor) Get(ctx context.Context, accessToken string) (*apimodel.WebPushSubscription, gtserror.WithCode) {
+ tokenID, errWithCode := p.getTokenID(ctx, accessToken)
+ if errWithCode != nil {
+ return nil, errWithCode
+ }
+
+ subscription, err := p.state.DB.GetWebPushSubscriptionByTokenID(ctx, tokenID)
+ if err != nil && !errors.Is(err, db.ErrNoEntries) {
+ err := gtserror.Newf("couldn't get Web Push subscription for token ID %s: %w", tokenID, err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+ if subscription == nil {
+ err := errors.New("no Web Push subscription exists for this access token")
+ return nil, gtserror.NewErrorNotFound(err)
+ }
+
+ return p.apiSubscription(ctx, subscription)
+}
diff --git a/internal/processing/push/push.go b/internal/processing/push/push.go
new file mode 100644
index 000000000..f46280386
--- /dev/null
+++ b/internal/processing/push/push.go
@@ -0,0 +1,85 @@
+// GoToSocial
+// Copyright (C) GoToSocial Authors admin@gotosocial.org
+// SPDX-License-Identifier: AGPL-3.0-or-later
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package push
+
+import (
+ "context"
+
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ "github.com/superseriousbusiness/gotosocial/internal/gtserror"
+ "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
+ "github.com/superseriousbusiness/gotosocial/internal/state"
+ "github.com/superseriousbusiness/gotosocial/internal/typeutils"
+)
+
+type Processor struct {
+ state *state.State
+ converter *typeutils.Converter
+}
+
+func New(state *state.State, converter *typeutils.Converter) Processor {
+ return Processor{
+ state: state,
+ converter: converter,
+ }
+}
+
+// getTokenID returns the token ID for a given access token.
+// Since all push API calls require authentication, this should always be available.
+func (p *Processor) getTokenID(ctx context.Context, accessToken string) (string, gtserror.WithCode) {
+ token, err := p.state.DB.GetTokenByAccess(ctx, accessToken)
+ if err != nil {
+ err := gtserror.Newf("couldn't find token ID for access token: %w", err)
+ return "", gtserror.NewErrorInternalError(err)
+ }
+
+ return token.ID, nil
+}
+
+// apiSubscription is a shortcut to return the API version of the given Web Push subscription,
+// or return an appropriate error if conversion fails.
+func (p *Processor) apiSubscription(ctx context.Context, subscription *gtsmodel.WebPushSubscription) (*apimodel.WebPushSubscription, gtserror.WithCode) {
+ apiSubscription, err := p.converter.WebPushSubscriptionToAPIWebPushSubscription(ctx, subscription)
+ if err != nil {
+ err := gtserror.Newf("error converting Web Push subscription %s to API representation: %w", subscription.ID, err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ return apiSubscription, nil
+}
+
+// alertsToNotificationFlags turns the alerts section of a push subscription API request into a packed bitfield.
+func alertsToNotificationFlags(alerts *apimodel.WebPushSubscriptionAlerts) gtsmodel.WebPushSubscriptionNotificationFlags {
+ var n gtsmodel.WebPushSubscriptionNotificationFlags
+
+ n.Set(gtsmodel.NotificationFollow, alerts.Follow)
+ n.Set(gtsmodel.NotificationFollowRequest, alerts.FollowRequest)
+ n.Set(gtsmodel.NotificationFavourite, alerts.Favourite)
+ n.Set(gtsmodel.NotificationMention, alerts.Mention)
+ n.Set(gtsmodel.NotificationReblog, alerts.Reblog)
+ n.Set(gtsmodel.NotificationPoll, alerts.Poll)
+ n.Set(gtsmodel.NotificationStatus, alerts.Status)
+ n.Set(gtsmodel.NotificationUpdate, alerts.Update)
+ n.Set(gtsmodel.NotificationAdminSignup, alerts.AdminSignup)
+ n.Set(gtsmodel.NotificationAdminReport, alerts.AdminReport)
+ n.Set(gtsmodel.NotificationPendingFave, alerts.PendingFavourite)
+ n.Set(gtsmodel.NotificationPendingReply, alerts.PendingReply)
+ n.Set(gtsmodel.NotificationPendingReblog, alerts.PendingReblog)
+
+ return n
+}
diff --git a/internal/processing/push/update.go b/internal/processing/push/update.go
new file mode 100644
index 000000000..370536f9b
--- /dev/null
+++ b/internal/processing/push/update.go
@@ -0,0 +1,63 @@
+// GoToSocial
+// Copyright (C) GoToSocial Authors admin@gotosocial.org
+// SPDX-License-Identifier: AGPL-3.0-or-later
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package push
+
+import (
+ "context"
+ "errors"
+
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ "github.com/superseriousbusiness/gotosocial/internal/db"
+ "github.com/superseriousbusiness/gotosocial/internal/gtserror"
+)
+
+// Update updates the Web Push subscription for the given access token.
+func (p *Processor) Update(
+ ctx context.Context,
+ accessToken string,
+ request *apimodel.WebPushSubscriptionUpdateRequest,
+) (*apimodel.WebPushSubscription, gtserror.WithCode) {
+ tokenID, errWithCode := p.getTokenID(ctx, accessToken)
+ if errWithCode != nil {
+ return nil, errWithCode
+ }
+
+ // Get existing subscription.
+ subscription, err := p.state.DB.GetWebPushSubscriptionByTokenID(ctx, tokenID)
+ if err != nil && !errors.Is(err, db.ErrNoEntries) {
+ err := gtserror.Newf("couldn't get Web Push subscription for token ID %s: %w", tokenID, err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+ if subscription == nil {
+ err := errors.New("no Web Push subscription exists for this access token")
+ return nil, gtserror.NewErrorNotFound(err)
+ }
+
+ // Update it.
+ subscription.NotificationFlags = alertsToNotificationFlags(request.Data.Alerts)
+ if err = p.state.DB.UpdateWebPushSubscription(
+ ctx,
+ subscription,
+ "notification_flags",
+ ); err != nil {
+ err := gtserror.Newf("couldn't update Web Push subscription for token ID %s: %w", tokenID, err)
+ return nil, gtserror.NewErrorInternalError(err)
+ }
+
+ return p.apiSubscription(ctx, subscription)
+}
diff --git a/internal/processing/timeline/notification.go b/internal/processing/timeline/notification.go
index a242c7b74..09636e7eb 100644
--- a/internal/processing/timeline/notification.go
+++ b/internal/processing/timeline/notification.go
@@ -184,7 +184,7 @@ func (p *Processor) notifVisible(
// If this is a new local account sign-up,
// skip normal visibility checking because
// origin account won't be confirmed yet.
- if n.NotificationType == gtsmodel.NotificationSignup {
+ if n.NotificationType == gtsmodel.NotificationAdminSignup {
return true, nil
}
diff --git a/internal/processing/workers/fromclientapi_test.go b/internal/processing/workers/fromclientapi_test.go
index d955f0529..acb25673d 100644
--- a/internal/processing/workers/fromclientapi_test.go
+++ b/internal/processing/workers/fromclientapi_test.go
@@ -179,6 +179,28 @@ func (suite *FromClientAPITestSuite) checkStreamed(
}
}
+// checkWebPushed asserts that the target account got a single Web Push notification with a given type.
+func (suite *FromClientAPITestSuite) checkWebPushed(
+ sender *testrig.WebPushMockSender,
+ accountID string,
+ notificationType gtsmodel.NotificationType,
+) {
+ pushedNotifications := sender.Sent[accountID]
+ if suite.Len(pushedNotifications, 1) {
+ pushedNotification := pushedNotifications[0]
+ suite.Equal(notificationType, pushedNotification.NotificationType)
+ }
+}
+
+// checkNotWebPushed asserts that the target account got no Web Push notifications.
+func (suite *FromClientAPITestSuite) checkNotWebPushed(
+ sender *testrig.WebPushMockSender,
+ accountID string,
+) {
+ pushedNotifications := sender.Sent[accountID]
+ suite.Len(pushedNotifications, 0)
+}
+
func (suite *FromClientAPITestSuite) statusJSON(
ctx context.Context,
typeConverter *typeutils.Converter,
@@ -341,6 +363,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWithNotification() {
string(notifJSON),
stream.EventTypeNotification,
)
+
+ // Check for a Web Push status notification.
+ suite.checkWebPushed(testStructs.WebPushSender, receivingAccount.ID, gtsmodel.NotificationStatus)
}
func (suite *FromClientAPITestSuite) TestProcessCreateStatusReply() {
@@ -409,6 +434,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusReply() {
statusJSON,
stream.EventTypeUpdate,
)
+
+ // Check for absence of Web Push notifications.
+ suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
}
func (suite *FromClientAPITestSuite) TestProcessCreateStatusReplyMuted() {
@@ -470,6 +498,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusReplyMuted() {
suite.ErrorIs(err, db.ErrNoEntries)
suite.Nil(notif)
+
+ // Check for absence of Web Push notifications.
+ suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
}
func (suite *FromClientAPITestSuite) TestProcessCreateStatusBoostMuted() {
@@ -531,6 +562,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusBoostMuted() {
suite.ErrorIs(err, db.ErrNoEntries)
suite.Nil(notif)
+
+ // Check for absence of Web Push notifications.
+ suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
}
func (suite *FromClientAPITestSuite) TestProcessCreateStatusListRepliesPolicyListOnlyOK() {
@@ -607,6 +641,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusListRepliesPolicyLis
statusJSON,
stream.EventTypeUpdate,
)
+
+ // Check for absence of Web Push notifications.
+ suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
}
func (suite *FromClientAPITestSuite) TestProcessCreateStatusListRepliesPolicyListOnlyNo() {
@@ -689,6 +726,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusListRepliesPolicyLis
"",
"",
)
+
+ // Check for absence of Web Push notifications.
+ suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
}
func (suite *FromClientAPITestSuite) TestProcessCreateStatusReplyListRepliesPolicyNone() {
@@ -765,6 +805,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusReplyListRepliesPoli
"",
"",
)
+
+ // Check for absence of Web Push notifications.
+ suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
}
func (suite *FromClientAPITestSuite) TestProcessCreateStatusBoost() {
@@ -829,6 +872,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusBoost() {
statusJSON,
stream.EventTypeUpdate,
)
+
+ // Check for absence of Web Push notifications.
+ suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
}
func (suite *FromClientAPITestSuite) TestProcessCreateStatusBoostNoReblogs() {
@@ -981,6 +1027,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWhichBeginsConversat
conversationJSON,
stream.EventTypeConversation,
)
+
+ // Check for a Web Push mention notification.
+ suite.checkWebPushed(testStructs.WebPushSender, receivingAccount.ID, gtsmodel.NotificationMention)
}
// A public message to a local user should not result in a conversation notification.
@@ -1050,6 +1099,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWhichShouldNotCreate
"",
"",
)
+
+ // Check for a Web Push mention notification.
+ suite.checkWebPushed(testStructs.WebPushSender, receivingAccount.ID, gtsmodel.NotificationMention)
}
// A public status with a hashtag followed by a local user who does not otherwise follow the author
@@ -1123,6 +1175,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWithFollowedHashtag(
"",
stream.EventTypeUpdate,
)
+
+ // Check for absence of Web Push notifications.
+ suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
}
// A public status with a hashtag followed by a local user who does not otherwise follow the author
@@ -1204,6 +1259,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWithFollowedHashtagA
"",
"",
)
+
+ // Check for absence of Web Push notifications.
+ suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
}
// A boost of a public status with a hashtag followed by a local user
@@ -1306,6 +1364,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateBoostWithFollowedHashtag()
"",
stream.EventTypeUpdate,
)
+
+ // Check for absence of Web Push notifications.
+ suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
}
// A boost of a public status with a hashtag followed by a local user
@@ -1416,6 +1477,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateBoostWithFollowedHashtagAn
"",
"",
)
+
+ // Check for absence of Web Push notifications.
+ suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
}
// A boost of a public status with a hashtag followed by a local user
@@ -1526,6 +1590,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateBoostWithFollowedHashtagAn
"",
"",
)
+
+ // Check for absence of Web Push notifications.
+ suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
}
// A public status with a hashtag followed by a local user who follows the author and has them on an exclusive list
@@ -1598,6 +1665,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWithAuthorOnExclusiv
"",
"",
)
+
+ // Check for absence of Web Push notifications.
+ suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
}
// A public status with a hashtag followed by a local user who follows the author and has them on an exclusive list
@@ -1712,6 +1782,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWithAuthorOnExclusiv
"",
"",
)
+
+ // Check for absence of Web Push notifications.
+ suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
}
// A public status with a hashtag followed by a local user who follows the author and has them on an exclusive list
@@ -1837,6 +1910,9 @@ func (suite *FromClientAPITestSuite) TestProcessCreateStatusWithAuthorOnExclusiv
"",
"",
)
+
+ // Check for a Web Push status notification.
+ suite.checkWebPushed(testStructs.WebPushSender, receivingAccount.ID, gtsmodel.NotificationStatus)
}
// Updating a public status with a hashtag followed by a local user who does not otherwise follow the author
@@ -1910,6 +1986,9 @@ func (suite *FromClientAPITestSuite) TestProcessUpdateStatusWithFollowedHashtag(
"",
stream.EventTypeStatusUpdate,
)
+
+ // Check for absence of Web Push notifications.
+ suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
}
func (suite *FromClientAPITestSuite) TestProcessStatusDelete() {
@@ -1963,6 +2042,9 @@ func (suite *FromClientAPITestSuite) TestProcessStatusDelete() {
stream.EventTypeDelete,
)
+ // Check for absence of Web Push notifications.
+ suite.checkNotWebPushed(testStructs.WebPushSender, receivingAccount.ID)
+
// Boost should no longer be in the database.
if !testrig.WaitFor(func() bool {
_, err := testStructs.State.DB.GetStatusByID(ctx, boostOfDeletedStatus.ID)
diff --git a/internal/processing/workers/fromfediapi_test.go b/internal/processing/workers/fromfediapi_test.go
index 88d0e6071..70886d698 100644
--- a/internal/processing/workers/fromfediapi_test.go
+++ b/internal/processing/workers/fromfediapi_test.go
@@ -240,7 +240,7 @@ func (suite *FromFediAPITestSuite) TestProcessFave() {
notif := &gtsmodel.Notification{}
err = testStructs.State.DB.GetWhere(context.Background(), where, notif)
suite.NoError(err)
- suite.Equal(gtsmodel.NotificationFave, notif.NotificationType)
+ suite.Equal(gtsmodel.NotificationFavourite, notif.NotificationType)
suite.Equal(fave.TargetAccountID, notif.TargetAccountID)
suite.Equal(fave.AccountID, notif.OriginAccountID)
suite.Equal(fave.StatusID, notif.StatusID)
@@ -313,7 +313,7 @@ func (suite *FromFediAPITestSuite) TestProcessFaveWithDifferentReceivingAccount(
notif := &gtsmodel.Notification{}
err = testStructs.State.DB.GetWhere(context.Background(), where, notif)
suite.NoError(err)
- suite.Equal(gtsmodel.NotificationFave, notif.NotificationType)
+ suite.Equal(gtsmodel.NotificationFavourite, notif.NotificationType)
suite.Equal(fave.TargetAccountID, notif.TargetAccountID)
suite.Equal(fave.AccountID, notif.OriginAccountID)
suite.Equal(fave.StatusID, notif.StatusID)
diff --git a/internal/processing/workers/surface.go b/internal/processing/workers/surface.go
index 4f6597b9a..4dc58c433 100644
--- a/internal/processing/workers/surface.go
+++ b/internal/processing/workers/surface.go
@@ -24,6 +24,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/processing/stream"
"github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
+ "github.com/superseriousbusiness/gotosocial/internal/webpush"
)
// Surface wraps functions for 'surfacing' the result
@@ -38,5 +39,6 @@ type Surface struct {
Stream *stream.Processor
VisFilter *visibility.Filter
EmailSender email.Sender
+ WebPushSender webpush.Sender
Conversations *conversations.Processor
}
diff --git a/internal/processing/workers/surfacenotify.go b/internal/processing/workers/surfacenotify.go
index 1520d2ec0..fdbd5e3c1 100644
--- a/internal/processing/workers/surfacenotify.go
+++ b/internal/processing/workers/surfacenotify.go
@@ -250,7 +250,7 @@ func (s *Surface) notifyFave(
// notify status author
// of fave by account.
if err := s.Notify(ctx,
- gtsmodel.NotificationFave,
+ gtsmodel.NotificationFavourite,
fave.TargetAccount,
fave.Account,
fave.StatusID,
@@ -521,7 +521,7 @@ func (s *Surface) notifySignup(ctx context.Context, newUser *gtsmodel.User) erro
var errs gtserror.MultiError
for _, mod := range modAccounts {
if err := s.Notify(ctx,
- gtsmodel.NotificationSignup,
+ gtsmodel.NotificationAdminSignup,
mod,
newUser.Account,
"",
@@ -647,5 +647,10 @@ func (s *Surface) Notify(
}
s.Stream.Notify(ctx, targetAccount, apiNotif)
+ // Send Web Push notification to the user.
+ if err = s.WebPushSender.Send(ctx, notif, filters, compiledMutes); err != nil {
+ return gtserror.Newf("error sending Web Push notifications: %w", err)
+ }
+
return nil
}
diff --git a/internal/processing/workers/surfacenotify_test.go b/internal/processing/workers/surfacenotify_test.go
index 52ee89e8b..6444314e2 100644
--- a/internal/processing/workers/surfacenotify_test.go
+++ b/internal/processing/workers/surfacenotify_test.go
@@ -45,6 +45,7 @@ func (suite *SurfaceNotifyTestSuite) TestSpamNotifs() {
Stream: testStructs.Processor.Stream(),
VisFilter: visibility.NewFilter(testStructs.State),
EmailSender: testStructs.EmailSender,
+ WebPushSender: testStructs.WebPushSender,
Conversations: testStructs.Processor.Conversations(),
}
diff --git a/internal/processing/workers/workers.go b/internal/processing/workers/workers.go
index ad673481b..9f37f554e 100644
--- a/internal/processing/workers/workers.go
+++ b/internal/processing/workers/workers.go
@@ -28,6 +28,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/processing/stream"
"github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
+ "github.com/superseriousbusiness/gotosocial/internal/webpush"
"github.com/superseriousbusiness/gotosocial/internal/workers"
)
@@ -44,6 +45,7 @@ func New(
converter *typeutils.Converter,
visFilter *visibility.Filter,
emailSender email.Sender,
+ webPushSender webpush.Sender,
account *account.Processor,
media *media.Processor,
stream *stream.Processor,
@@ -65,6 +67,7 @@ func New(
Stream: stream,
VisFilter: visFilter,
EmailSender: emailSender,
+ WebPushSender: webPushSender,
Conversations: conversations,
}