diff --git a/.env_sample b/.env_sample index 4ed89fac4..8723203f1 100644 --- a/.env_sample +++ b/.env_sample @@ -120,8 +120,6 @@ PUSHER_APP_ID= PUSHER_CLUSTER= PUSHER_KEY= PUSHER_SECRET= -PUSHER_BEAMS_ID= -PUSHER_BEAMS_KEY= # Google recaptcha (Optional) # (https://developers.google.com/recaptcha/intro) diff --git a/Gemfile b/Gemfile index 9197f872c..99f06497b 100644 --- a/Gemfile +++ b/Gemfile @@ -70,7 +70,6 @@ gem "pg_search", "~> 2.3.5" # PgSearch builds Active Record named scopes that ta gem "puma", "~> 5.2.2" # Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server gem "pundit", "~> 2.1" # Object oriented authorization for Rails applications gem "pusher", "~> 2.0" # Ruby library for Pusher Channels HTTP API -gem "pusher-push-notifications", "~> 2.0" # Pusher Push Notifications Ruby server SDK gem "rack-attack", "~> 6.5.0" # Used to throttle requests to prevent brute force attacks gem "rack-cors", "~> 1.1" # Middleware that will make Rack-based apps CORS compatible gem "rack-timeout", "~> 0.6" # Rack middleware which aborts requests that have been running for longer than a specified timeout diff --git a/Gemfile.lock b/Gemfile.lock index a79078ca4..3c22f5200 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -526,9 +526,6 @@ GEM httpclient (~> 2.8) multi_json (~> 1.15) pusher-signature (~> 0.1.8) - pusher-push-notifications (2.0.1) - jwt (~> 2.1, >= 2.1.0) - rest-client (~> 2.0, >= 2.0.2) pusher-signature (0.1.8) raabro (1.4.0) racc (1.5.2) @@ -933,7 +930,6 @@ DEPENDENCIES pundit (~> 2.1) pundit-matchers (~> 1.6) pusher (~> 2.0) - pusher-push-notifications (~> 2.0) rack-attack (~> 6.5.0) rack-cors (~> 1.1) rack-host-redirect (~> 1.3) diff --git a/app/models/admin_menu.rb b/app/models/admin_menu.rb index 85d1d95c4..b5941a719 100644 --- a/app/models/admin_menu.rb +++ b/app/models/admin_menu.rb @@ -54,6 +54,7 @@ class AdminMenu scope :apps, "palette-line", [ item(name: "chat channels"), + item(name: "consumer apps", controller: "consumer_apps"), item(name: "events"), item(name: "listings"), item(name: "welcome"), diff --git a/app/services/notifications/new_comment/send.rb b/app/services/notifications/new_comment/send.rb index 4a96def62..38fac21a0 100644 --- a/app/services/notifications/new_comment/send.rb +++ b/app/services/notifications/new_comment/send.rb @@ -34,27 +34,17 @@ module Notifications ) end + # Send PNs using Rpush - respecting users' notificaton delivery settings targets = User.where(id: user_ids, mobile_comment_notifications: true).ids - - # Pusher Beams uses named Pub/Sub channels instead of raw user_ids - target_channels = targets.map { |id| "user-notifications-#{id}" } - # Sends the push notification to Pusher Beams channels. - # Batch is in place to respect Pusher 100 channel limit. - target_channels.each_slice(100) { |batch| send_push_notifications(batch) } - - if FeatureFlag.enabled?(:mobile_notifications) - # Send PNs using Rpush - url_path = Rails.application.routes.url_helpers.notifications_path(:comments) - PushNotifications::Send.call( - user_ids: targets, - title: "@#{comment.user.username}", - body: "Re: #{comment.parent_or_root_article.title.strip}", - payload: { - url: URL.url(url_path), - type: "new comment" - }, - ) - end + PushNotifications::Send.call( + user_ids: targets, + title: "@#{comment.user.username}", + body: "Re: #{comment.parent_or_root_article.title.strip}", + payload: { + url: URL.url(comment.path), + type: "new comment" + }, + ) return unless comment.commentable.organization_id @@ -97,40 +87,6 @@ module Notifications user_ids_for("only_author_comments") end - - def send_push_notifications(channels) - return unless ApplicationConfig["PUSHER_BEAMS_KEY"] && ApplicationConfig["PUSHER_BEAMS_KEY"].size == 64 - - Pusher::PushNotifications.publish_to_interests( - interests: channels, - payload: push_notification_payload, - ) - end - - def push_notification_payload - title = "@#{comment.user.username}" - subtitle = "re: #{comment.parent_or_root_article.title.strip}" - data_payload = { url: URL.url("/notifications/comments") } - { - apns: { - aps: { - alert: { - title: title, - subtitle: subtitle, - body: CGI.unescapeHTML(comment.title.strip) - } - }, - data: data_payload - }, - fcm: { - notification: { - title: title, - body: subtitle - }, - data: data_payload - } - } - end end end end diff --git a/app/services/push_notifications/send.rb b/app/services/push_notifications/send.rb index 036310b00..d9e4b0a73 100644 --- a/app/services/push_notifications/send.rb +++ b/app/services/push_notifications/send.rb @@ -12,8 +12,6 @@ module PushNotifications end def call - return unless FeatureFlag.enabled?(:mobile_notifications) - Device.where(user_id: @user_ids).find_each do |device| device.create_notification(@title, @body, @payload) end diff --git a/app/views/admin/consumer_apps/index.html.erb b/app/views/admin/consumer_apps/index.html.erb index d9f0ea026..e693f8f03 100644 --- a/app/views/admin/consumer_apps/index.html.erb +++ b/app/views/admin/consumer_apps/index.html.erb @@ -4,7 +4,10 @@

Consumer Apps

- The following are apps that will be able to receive Push Notifications from <%= SiteConfig.community_name %>. + Consumer apps are standalone mobile apps that users can use to browse <%= SiteConfig.community_name %> as an alternative to web browsers. +

+

+ If the app is listed as operational, it means <%= SiteConfig.community_name %> will be able to deliver push notifications directly to users' devices.

diff --git a/config/initializers/pusher.rb b/config/initializers/pusher.rb index 7b0dfbf9a..4577408a6 100644 --- a/config/initializers/pusher.rb +++ b/config/initializers/pusher.rb @@ -1,5 +1,3 @@ -require "pusher/push_notifications" - if ApplicationConfig["PUSHER_APP_ID"].present? Pusher.app_id = ApplicationConfig["PUSHER_APP_ID"] Pusher.key = ApplicationConfig["PUSHER_KEY"] @@ -7,9 +5,4 @@ if ApplicationConfig["PUSHER_APP_ID"].present? Pusher.cluster = ApplicationConfig["PUSHER_CLUSTER"] Pusher.logger = Rails.logger Pusher.encrypted = true - - Pusher::PushNotifications.configure do |config| - config.instance_id = ApplicationConfig["PUSHER_BEAMS_ID"] - config.secret_key = ApplicationConfig["PUSHER_BEAMS_KEY"] - end end diff --git a/config/routes.rb b/config/routes.rb index 5095553f4..ad8f4c6e9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -134,10 +134,8 @@ Rails.application.routes.draw do end resources :comment_mutes, only: %i[update] resources :users, only: %i[index], defaults: { format: :json } do # internal API - constraints(-> { FeatureFlag.enabled?(:mobile_notifications) }) do - collection do - resources :devices, only: %i[create destroy] - end + collection do + resources :devices, only: %i[create destroy] end end resources :users, only: %i[update] diff --git a/config/routes/admin.rb b/config/routes/admin.rb index f54c0defb..7d2549595 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -14,7 +14,6 @@ namespace :admin do resources :organization_memberships, only: %i[update destroy create] resources :permissions, only: %i[index] resources :reactions, only: [:update] - resources :consumer_apps, only: %i[index new create edit update destroy] namespace :settings do resources :authentications, only: [:create] resources :campaigns, only: [:create] @@ -123,6 +122,7 @@ namespace :admin do delete :remove_user end end + resources :consumer_apps, only: %i[index new create edit update destroy] resources :events, only: %i[index create update new edit] resources :listings, only: %i[index edit update destroy] resources :listing_categories, only: %i[index edit update new create diff --git a/docs/backend/push-notifications.md b/docs/backend/push-notifications.md new file mode 100644 index 000000000..969dce15f --- /dev/null +++ b/docs/backend/push-notifications.md @@ -0,0 +1,27 @@ +--- +title: Push Notifications +--- + +# Push Notifications Delivery + +Forem instances rely on [Rpush](https://github.com/rpush/rpush) to deliver push +notifications. This decision was heavily influenced by the desire to +provide as much flexibility as possible to Creators. In order to do this, Forem +instances can register and configure a `ConsumerApp`. + +These consumer apps represent mobile applications that users use to browse and consume content +on a Forem. Authenticated users of a specific Forem instance can register a `Device` +associated to a `ConsumerApp`. With these pieces we are able to deliver +push notifications to users devices. + +The `ConsumerApp` configuration page can be found at +`/admin/apps/consumer_apps`. The official Forem apps are supported by default +and require their secret credential to be provided via ENV variable. + +## Rpush Implementation + +We use Rpush's `rpush-redis` implementation (read +[this thread](https://github.com/forem/forem/pull/12419/files#r564660917) for +the reasons why), hence all `Rpush` models are persisted in Redis. More +details about how this works +[here](https://github.com/rpush/rpush/wiki/Using-Redis). diff --git a/docs/backend/pusher.md b/docs/backend/pusher.md index 5d67b465a..4b066f96e 100644 --- a/docs/backend/pusher.md +++ b/docs/backend/pusher.md @@ -40,68 +40,3 @@ you'll need to provide those keys to the Rails application. ![pusher-3](https://user-images.githubusercontent.com/22895284/51086058-0626d780-1742-11e9-9c2a-26b9b10fa77f.png) 5. Done. - -## Mobile Push Notifications - -These steps are required only when working with the native Apps. In order to -setup Push Notifications to mobile devices you need to create a Pusher Beams -instance and retrieve its credentials - -1. [Sign up](https://dashboard.pusher.com/accounts/sign_up) or - [sign in](https://dashboard.pusher.com/) to your Pusher account. - -2. Once signed in, go in the "BEAMS" section on the left sidebar and fill in the - prompt to create a new Pusher Beams instance. - -![pusher-beams-1](https://user-images.githubusercontent.com/6045239/78602158-2e9d6480-7813-11ea-9da1-4f1310776570.png) - -3. In your new Pusher Beams instance, click the "Credentials" tab. - - ![pusher-beams-2](https://user-images.githubusercontent.com/6045239/78602934-82f51400-7814-11ea-8cc8-6933c43079bc.png) - -4. Change your keys accordingly (name of Pusher key -> name of our application - key): - -```text -Instance ID -> PUSHER_BEAMS_ID -Instance Key -> PUSHER_BEAMS_KEY -``` - -5. Done. You now have your server configured to use Pusher Beams. - -However, in order to send Push Notifications to devices you'll need to do some -platform specific configuration as well. - -### Firebase for Android Push Notifications - -1. Sign up or Sign in to [Firebase](https://firebase.google.com/) - -2. Add a new project. Google Analytics doesn't need to be configured for this - Push Notifications to work. - -![fcm-1](https://user-images.githubusercontent.com/6045239/78604287-cfd9ea00-7816-11ea-9b47-b374b4e60faf.png) - -![fcm-2](https://user-images.githubusercontent.com/6045239/78609833-d2d9d800-7820-11ea-9d78-53dfe823bd3f.png) - -3. Add Android support for your iOS app - -![fcm-3](https://user-images.githubusercontent.com/6045239/78609955-1b919100-7821-11ea-8a2f-9f96cf26185c.png) - -![fcm-4](https://user-images.githubusercontent.com/6045239/78612418-885b5a00-7826-11ea-91b8-125981367068.png) - -4. Download the `google-services.json` file (to be used in the Android app) - -![fcm-5](https://user-images.githubusercontent.com/6045239/78612412-85f90000-7826-11ea-8996-dcb568e7b037.png) - -5. Finish off the next steps (not required at this moment). Now head over to - your Firebase project's settings page and copy the Server key from the "Cloud - Messaging" tab - -![fcm-6](https://user-images.githubusercontent.com/6045239/78615948-11c35a00-7830-11ea-8470-675488a0ecc1.png) - -6. Now back in your Pusher Beams page account add the Server key from the - previous step under the "Settings" tab - -![fcm-7](https://user-images.githubusercontent.com/6045239/78616350-353ad480-7831-11ea-8d63-2f183c2f233b.png) - -7. Done. diff --git a/docs/backend/readme.md b/docs/backend/readme.md index 553924150..d09a2e2d7 100644 --- a/docs/backend/readme.md +++ b/docs/backend/readme.md @@ -19,6 +19,7 @@ items: - notification.md - scheduled-jobs.md - metrics.md + - push-notifications.md - tracking.md - service-objects.md --- diff --git a/spec/requests/devices_spec.rb b/spec/requests/devices_spec.rb index 1fd390a38..69fd04850 100644 --- a/spec/requests/devices_spec.rb +++ b/spec/requests/devices_spec.rb @@ -5,7 +5,6 @@ RSpec.describe "Devices", type: :request do let(:consumer_app) { create(:consumer_app) } before do - allow(FeatureFlag).to receive(:enabled?).with(:mobile_notifications).and_return(true) sign_in user end diff --git a/spec/routing/devices_routes_spec.rb b/spec/routing/devices_routes_spec.rb deleted file mode 100644 index 6e67f1c5c..000000000 --- a/spec/routing/devices_routes_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -require "rails_helper" - -RSpec.describe "User devices routes", type: :routing do - it "renders the user devices routes if the mobile_notifications feature flag is enabled", :aggregate_failures do - allow(FeatureFlag).to receive(:enabled?).twice.with(:mobile_notifications).and_return(true) - - expect(post: devices_path).to route_to( - controller: "devices", - action: "create", - format: :json, - locale: nil, - ) - - expect(delete: "#{devices_path}/1").to route_to( - controller: "devices", - action: "destroy", - id: "1", - format: :json, - locale: nil, - ) - end - - it "does not render the user devices routes if the mobile_notifications feature flag is disabled", - :aggregate_failures do - allow(FeatureFlag).to receive(:enabled?).at_least(:twice).with(:mobile_notifications).and_return(false) - - expect(post: devices_path).not_to route_to( - controller: "devices", - action: "create", - format: :json, - locale: nil, - ) - - expect(delete: "#{devices_path}/1").not_to route_to( - controller: "devices", - action: "destroy", - id: "1", - format: :json, - locale: nil, - ) - end -end diff --git a/spec/services/notifications/new_comment/send_spec.rb b/spec/services/notifications/new_comment/send_spec.rb index 528803160..8a930063c 100644 --- a/spec/services/notifications/new_comment/send_spec.rb +++ b/spec/services/notifications/new_comment/send_spec.rb @@ -97,22 +97,4 @@ RSpec.describe Notifications::NewComment::Send, type: :service do expect(Notification.where(notifiable_type: "Comment", notifiable_id: child_comment.id, organization_id: organization.id)).to be_any end - - it "sends Push Notifications using Pusher Beams when configured" do - allow(ApplicationConfig).to receive(:[]).with("PUSHER_BEAMS_KEY").and_return("x" * 64) - allow(ApplicationConfig).to receive(:[]).with("APP_PROTOCOL").and_return("http://") - allow(ApplicationConfig).to receive(:[]).with("APP_DOMAIN").and_return("localhost:3000") - - allow(Pusher::PushNotifications).to receive(:publish_to_interests) - - comment_sent = child_comment - described_class.call(comment_sent) - - channels = ["user-notifications-#{user2.id}", "user-notifications-#{user.id}"] - payload = described_class.new(comment_sent).__send__(:push_notification_payload) - expect(Pusher::PushNotifications).to have_received(:publish_to_interests) do |block| - expect(block[:interests]).to match_array(channels) - expect(block[:payload]).to eq(payload) - end - end end diff --git a/spec/services/push_notifications/send_spec.rb b/spec/services/push_notifications/send_spec.rb index fc8a31672..3f763e11a 100644 --- a/spec/services/push_notifications/send_spec.rb +++ b/spec/services/push_notifications/send_spec.rb @@ -20,18 +20,8 @@ RSpec.describe PushNotifications::Send, type: :service do } end - context "with feature flag set to false/off" do - before { allow(FeatureFlag).to receive(:enabled?).with(:mobile_notifications).and_return(false) } - - it "does nothing if the feature flag is disabled" do - expect { described_class.call(**params) } - .not_to change { Rpush::Client::Redis::Notification.all.count } - end - end - context "with no devices for user" do before do - allow(FeatureFlag).to receive(:enabled?).with(:mobile_notifications).and_return(true) user.devices.delete end @@ -44,7 +34,6 @@ RSpec.describe PushNotifications::Send, type: :service do context "with devices for one user" do before do - allow(FeatureFlag).to receive(:enabled?).with(:mobile_notifications).and_return(true) allow(ApplicationConfig).to receive(:[]).with("RPUSH_IOS_PEM").and_return("dGVzdGluZw==") allow(ApplicationConfig).to receive(:[]).with("COMMUNITY_NAME").and_return("Forem") create(:device, user: user) @@ -67,7 +56,6 @@ RSpec.describe PushNotifications::Send, type: :service do context "with devices for multiple users" do before do - allow(FeatureFlag).to receive(:enabled?).with(:mobile_notifications).and_return(true) allow(ApplicationConfig).to receive(:[]).with("RPUSH_IOS_PEM").and_return("dGVzdGluZw==") allow(ApplicationConfig).to receive(:[]).with("COMMUNITY_NAME").and_return("Forem") create(:device, user: user) diff --git a/vendor/cache/pusher-push-notifications-2.0.1.gem b/vendor/cache/pusher-push-notifications-2.0.1.gem deleted file mode 100644 index 10b893007..000000000 Binary files a/vendor/cache/pusher-push-notifications-2.0.1.gem and /dev/null differ