actionwebpush 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/CODE_OF_CONDUCT.md +84 -0
  4. data/CONTRIBUTING.md +256 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +569 -0
  7. data/Rakefile +17 -0
  8. data/app/controllers/actionwebpush/subscriptions_controller.rb +60 -0
  9. data/app/models/actionwebpush/subscription.rb +164 -0
  10. data/config/routes.rb +5 -0
  11. data/db/migrate/001_create_action_web_push_subscriptions.rb +17 -0
  12. data/lib/actionwebpush/analytics.rb +197 -0
  13. data/lib/actionwebpush/authorization.rb +236 -0
  14. data/lib/actionwebpush/base.rb +107 -0
  15. data/lib/actionwebpush/batch_delivery.rb +92 -0
  16. data/lib/actionwebpush/configuration.rb +91 -0
  17. data/lib/actionwebpush/delivery_job.rb +52 -0
  18. data/lib/actionwebpush/delivery_methods/base.rb +19 -0
  19. data/lib/actionwebpush/delivery_methods/test.rb +36 -0
  20. data/lib/actionwebpush/delivery_methods/web_push.rb +74 -0
  21. data/lib/actionwebpush/engine.rb +20 -0
  22. data/lib/actionwebpush/error_handler.rb +99 -0
  23. data/lib/actionwebpush/generators/campfire_migration_generator.rb +69 -0
  24. data/lib/actionwebpush/generators/install_generator.rb +47 -0
  25. data/lib/actionwebpush/generators/templates/campfire_compatibility.rb +173 -0
  26. data/lib/actionwebpush/generators/templates/campfire_data_migration.rb +98 -0
  27. data/lib/actionwebpush/generators/templates/create_action_web_push_subscriptions.rb +17 -0
  28. data/lib/actionwebpush/generators/templates/initializer.rb +16 -0
  29. data/lib/actionwebpush/generators/vapid_keys_generator.rb +38 -0
  30. data/lib/actionwebpush/instrumentation.rb +31 -0
  31. data/lib/actionwebpush/logging.rb +38 -0
  32. data/lib/actionwebpush/metrics.rb +67 -0
  33. data/lib/actionwebpush/notification.rb +97 -0
  34. data/lib/actionwebpush/pool.rb +167 -0
  35. data/lib/actionwebpush/railtie.rb +48 -0
  36. data/lib/actionwebpush/rate_limiter.rb +167 -0
  37. data/lib/actionwebpush/sentry_integration.rb +104 -0
  38. data/lib/actionwebpush/status_broadcaster.rb +62 -0
  39. data/lib/actionwebpush/status_channel.rb +21 -0
  40. data/lib/actionwebpush/tenant_configuration.rb +106 -0
  41. data/lib/actionwebpush/test_helper.rb +68 -0
  42. data/lib/actionwebpush/version.rb +5 -0
  43. data/lib/actionwebpush.rb +78 -0
  44. data/sig/actionwebpush.rbs +4 -0
  45. metadata +212 -0
data/README.md ADDED
@@ -0,0 +1,569 @@
1
+ # ActionWebPush
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/actionwebpush.svg)](https://badge.fury.io/rb/actionwebpush)
4
+ [![Build Status](https://github.com/your-org/actionwebpush/workflows/CI/badge.svg)](https://github.com/your-org/actionwebpush/actions)
5
+
6
+ Rails integration for Web Push notifications with ActionMailer-like interface.
7
+
8
+ ActionWebPush provides a comprehensive solution for sending Web Push notifications in Rails applications. It offers an ActionMailer-inspired API, background job integration, rate limiting, and sophisticated error handling.
9
+
10
+ Extracted from the [Campfire](https://github.com/your-org/once-campfire) project and designed for production use.
11
+
12
+ ## Features
13
+
14
+ - ๐Ÿš€ **ActionMailer-like Interface** - Familiar Rails patterns for sending notifications
15
+ - ๐Ÿ”ง **Easy Configuration** - Simple setup with VAPID keys
16
+ - ๐ŸŽฏ **Background Jobs** - ActiveJob integration for async delivery
17
+ - ๐Ÿ“Š **Rate Limiting** - Built-in protection against abuse
18
+ - ๐Ÿ”„ **Thread Pool Management** - Efficient concurrent delivery
19
+ - ๐Ÿ“ˆ **Instrumentation** - ActiveSupport::Notifications integration
20
+ - ๐Ÿ›ก๏ธ **Error Handling** - Comprehensive error management with cleanup
21
+ - ๐Ÿ—„๏ธ **ActiveRecord Integration** - Models and migrations included
22
+ - ๐Ÿงช **Test Helpers** - Testing utilities for development
23
+ - ๐Ÿ“ **Detailed Logging** - Structured logging for debugging
24
+
25
+ ## Table of Contents
26
+
27
+ - [Installation](#installation)
28
+ - [Configuration](#configuration)
29
+ - [Quick Start](#quick-start)
30
+ - [Usage](#usage)
31
+ - [Basic Notifications](#basic-notifications)
32
+ - [ActionMailer-like Senders](#actionmailer-like-senders)
33
+ - [Background Delivery](#background-delivery)
34
+ - [Batch Operations](#batch-operations)
35
+ - [Subscription Management](#subscription-management)
36
+ - [Rate Limiting](#rate-limiting)
37
+ - [Error Handling](#error-handling)
38
+ - [Monitoring](#monitoring)
39
+ - [Testing](#testing)
40
+ - [Configuration Reference](#configuration-reference)
41
+ - [API Documentation](#api-documentation)
42
+ - [Contributing](#contributing)
43
+ - [License](#license)
44
+
45
+ ## Installation
46
+
47
+ Add this line to your application's Gemfile:
48
+
49
+ ```ruby
50
+ gem 'actionwebpush'
51
+ ```
52
+
53
+ And then execute:
54
+
55
+ ```bash
56
+ bundle install
57
+ ```
58
+
59
+ Run the installation generator:
60
+
61
+ ```bash
62
+ rails generate action_web_push:install
63
+ ```
64
+
65
+ This will:
66
+ - Create configuration file `config/initializers/action_web_push.rb`
67
+ - Generate database migrations for subscription management
68
+ - Create VAPID keys if not present
69
+
70
+ Run the migrations:
71
+
72
+ ```bash
73
+ rails db:migrate
74
+ ```
75
+
76
+ ## Configuration
77
+
78
+ Configure ActionWebPush in `config/initializers/action_web_push.rb`:
79
+
80
+ ```ruby
81
+ ActionWebPush.configure do |config|
82
+ # Required: VAPID keys for push service authentication
83
+ config.vapid_public_key = ENV['VAPID_PUBLIC_KEY']
84
+ config.vapid_private_key = ENV['VAPID_PRIVATE_KEY']
85
+ config.vapid_subject = 'mailto:support@yourapp.com'
86
+
87
+ # Optional: Performance tuning
88
+ config.pool_size = 10 # Thread pool size
89
+ config.queue_size = 100 # Queue size
90
+ config.connection_pool_size = 5 # HTTP connection pool
91
+ config.batch_size = 100 # Default batch size
92
+
93
+ # Optional: Delivery method (default: :web_push)
94
+ config.delivery_method = :web_push
95
+ end
96
+ ```
97
+
98
+ ### Generate VAPID Keys
99
+
100
+ If you don't have VAPID keys, generate them:
101
+
102
+ ```bash
103
+ rails generate action_web_push:vapid_keys
104
+ ```
105
+
106
+ This creates a `.env` file with your keys. Add them to your production environment:
107
+
108
+ ```bash
109
+ # .env
110
+ VAPID_PUBLIC_KEY=your_generated_public_key
111
+ VAPID_PRIVATE_KEY=your_generated_private_key
112
+ ```
113
+
114
+ ## Quick Start
115
+
116
+ ### 1. Set up User Associations
117
+
118
+ ```ruby
119
+ class User < ApplicationRecord
120
+ has_many :push_subscriptions,
121
+ class_name: "ActionWebPush::Subscription",
122
+ foreign_key: :user_id,
123
+ dependent: :destroy
124
+ end
125
+ ```
126
+
127
+ ### 2. Create Notification Senders
128
+
129
+ ```ruby
130
+ class UserNotifications < ActionWebPush::Base
131
+ def welcome(user)
132
+ web_push(
133
+ title: "Welcome to MyApp!",
134
+ body: "Thanks for joining us",
135
+ to: user.push_subscriptions,
136
+ data: { type: 'welcome', url: '/dashboard' }
137
+ )
138
+ end
139
+
140
+ def new_message(user, message)
141
+ web_push(
142
+ title: "New Message",
143
+ body: message.preview,
144
+ to: user.push_subscriptions,
145
+ data: {
146
+ type: 'message',
147
+ message_id: message.id,
148
+ url: message_path(message)
149
+ }
150
+ )
151
+ end
152
+ end
153
+ ```
154
+
155
+ ### 3. Send Notifications
156
+
157
+ ```ruby
158
+ # Deliver immediately
159
+ UserNotifications.welcome(user).deliver_now
160
+
161
+ # Deliver via background job (recommended)
162
+ UserNotifications.new_message(user, message).deliver_later
163
+
164
+ # Batch delivery to multiple users
165
+ users = User.active.includes(:push_subscriptions)
166
+ users.each do |user|
167
+ UserNotifications.welcome(user).deliver_later
168
+ end
169
+ ```
170
+
171
+ ## Usage
172
+
173
+ ### Basic Notifications
174
+
175
+ Create and send notifications directly:
176
+
177
+ ```ruby
178
+ notification = ActionWebPush::Notification.new(
179
+ title: "System Alert",
180
+ body: "Maintenance scheduled for tonight",
181
+ endpoint: subscription.endpoint,
182
+ p256dh_key: subscription.p256dh_key,
183
+ auth_key: subscription.auth_key,
184
+ data: { type: 'maintenance', scheduled_at: '2024-01-01T02:00:00Z' },
185
+ icon: '/system-icon.png',
186
+ badge: '/badge.png',
187
+ urgency: 'high'
188
+ )
189
+
190
+ # Synchronous delivery
191
+ notification.deliver_now
192
+
193
+ # Asynchronous delivery
194
+ notification.deliver_later
195
+ ```
196
+
197
+ ### ActionMailer-like Senders
198
+
199
+ Create notification classes that inherit from `ActionWebPush::Base`:
200
+
201
+ ```ruby
202
+ class SystemNotifications < ActionWebPush::Base
203
+ default data: { app: 'MyApp' }
204
+
205
+ def maintenance_notice(users, maintenance)
206
+ web_push(
207
+ title: "Scheduled Maintenance",
208
+ body: "Service will be unavailable #{maintenance.start_time}",
209
+ to: users.flat_map(&:push_subscriptions),
210
+ data: {
211
+ type: 'maintenance',
212
+ start_time: maintenance.start_time.iso8601,
213
+ duration: maintenance.duration_minutes,
214
+ url: maintenance_path(maintenance)
215
+ },
216
+ urgency: 'high'
217
+ )
218
+ end
219
+
220
+ def feature_announcement(user, feature)
221
+ web_push(
222
+ title: "New Feature Available!",
223
+ body: feature.description,
224
+ to: user.push_subscriptions,
225
+ data: {
226
+ type: 'feature',
227
+ feature_id: feature.id,
228
+ url: feature_path(feature)
229
+ },
230
+ icon: feature.icon_url
231
+ )
232
+ end
233
+ end
234
+ ```
235
+
236
+ ### Background Delivery
237
+
238
+ Leverage ActiveJob for asynchronous delivery:
239
+
240
+ ```ruby
241
+ # Basic background delivery
242
+ notification.deliver_later
243
+
244
+ # With scheduling
245
+ notification.deliver_later(wait: 1.hour)
246
+ notification.deliver_later(wait_until: Date.tomorrow.noon)
247
+
248
+ # Custom queue and priority
249
+ notification.deliver_later(
250
+ queue: :critical_notifications,
251
+ priority: 10
252
+ )
253
+
254
+ # ActionMailer-like senders
255
+ UserNotifications.welcome(user).deliver_later(wait: 5.minutes)
256
+ ```
257
+
258
+ ### Batch Operations
259
+
260
+ Efficiently send to multiple recipients:
261
+
262
+ ```ruby
263
+ # Using BatchDelivery for performance
264
+ notifications = users.map do |user|
265
+ user.push_subscriptions.map do |subscription|
266
+ subscription.build_notification(
267
+ title: "Weekly Update",
268
+ body: "Check out this week's highlights",
269
+ data: { type: 'weekly_update' }
270
+ )
271
+ end
272
+ end.flatten
273
+
274
+ ActionWebPush::BatchDelivery.deliver(notifications)
275
+
276
+ # With custom batch size
277
+ ActionWebPush::BatchDelivery.new(notifications, batch_size: 50).deliver_all
278
+
279
+ # Using ActionMailer-like pattern for batches
280
+ users.find_each do |user|
281
+ WeeklyNotifications.digest(user).deliver_later
282
+ end
283
+ ```
284
+
285
+ ## Subscription Management
286
+
287
+ ActionWebPush includes ActiveRecord models for managing subscriptions:
288
+
289
+ ```ruby
290
+ # Create subscription from frontend
291
+ subscription = ActionWebPush::Subscription.create!(
292
+ endpoint: params[:endpoint],
293
+ p256dh_key: params[:keys][:p256dh],
294
+ auth_key: params[:keys][:auth],
295
+ user: current_user,
296
+ user_agent: request.user_agent
297
+ )
298
+
299
+ # Find subscriptions
300
+ user_subscriptions = ActionWebPush::Subscription.for_user(current_user)
301
+ active_subscriptions = ActionWebPush::Subscription.active
302
+ mobile_subscriptions = ActionWebPush::Subscription.by_user_agent('Mobile')
303
+
304
+ # Build notifications from subscriptions
305
+ notification = subscription.build_notification(
306
+ title: "Hello",
307
+ body: "World",
308
+ data: { url: '/dashboard' }
309
+ )
310
+
311
+ # Cleanup expired subscriptions
312
+ ActionWebPush::Subscription.expired.destroy_all
313
+ ```
314
+
315
+ ### Frontend Integration
316
+
317
+ Example JavaScript for subscription management:
318
+
319
+ ```javascript
320
+ // Register service worker and get subscription
321
+ navigator.serviceWorker.register('/sw.js').then(registration => {
322
+ return registration.pushManager.subscribe({
323
+ userVisibleOnly: true,
324
+ applicationServerKey: '<%= ActionWebPush.config.vapid_public_key %>'
325
+ });
326
+ }).then(subscription => {
327
+ // Send subscription to your Rails app
328
+ fetch('/push_subscriptions', {
329
+ method: 'POST',
330
+ headers: {
331
+ 'Content-Type': 'application/json',
332
+ 'X-CSRF-Token': document.querySelector('[name="csrf-token"]').content
333
+ },
334
+ body: JSON.stringify({
335
+ subscription: {
336
+ endpoint: subscription.endpoint,
337
+ keys: {
338
+ p256dh: arrayBufferToBase64(subscription.getKey('p256dh')),
339
+ auth: arrayBufferToBase64(subscription.getKey('auth'))
340
+ }
341
+ }
342
+ })
343
+ });
344
+ });
345
+ ```
346
+
347
+ ## Rate Limiting
348
+
349
+ Protect your application from abuse with built-in rate limiting:
350
+
351
+ ```ruby
352
+ # Configure rate limits
353
+ rate_limiter = ActionWebPush::RateLimiter.new(
354
+ limits: {
355
+ endpoint: { max_requests: 100, window: 3600 }, # 100/hour per endpoint
356
+ user: { max_requests: 1000, window: 3600 }, # 1000/hour per user
357
+ subscription: { max_requests: 50, window: 3600 } # 50/hour per subscription
358
+ }
359
+ )
360
+
361
+ # Check before sending
362
+ begin
363
+ rate_limiter.check_rate_limit!(:user, current_user.id)
364
+ notification.deliver_now
365
+ rescue ActionWebPush::RateLimitExceeded => e
366
+ render json: { error: "Rate limit exceeded" }, status: 429
367
+ end
368
+
369
+ # Get rate limit information
370
+ info = rate_limiter.rate_limit_info(:user, current_user.id)
371
+ # => { limit: 1000, remaining: 950, window: 3600, reset_at: Time }
372
+ ```
373
+
374
+ ## Error Handling
375
+
376
+ ActionWebPush provides comprehensive error handling:
377
+
378
+ ```ruby
379
+ begin
380
+ notification.deliver_now
381
+ rescue ActionWebPush::ExpiredSubscriptionError => e
382
+ # Subscription is no longer valid - cleanup
383
+ subscription.destroy
384
+ rescue ActionWebPush::RateLimitExceeded => e
385
+ # Rate limit hit - retry later
386
+ notification.deliver_later(wait: 1.hour)
387
+ rescue ActionWebPush::DeliveryError => e
388
+ # Delivery failed - log and handle
389
+ Rails.logger.error "Push notification failed: #{e.message}"
390
+ rescue ActionWebPush::ConfigurationError => e
391
+ # Configuration issue - check VAPID keys
392
+ Rails.logger.error "Push configuration error: #{e.message}"
393
+ end
394
+ ```
395
+
396
+ All errors include detailed context and are automatically instrumented for monitoring.
397
+
398
+ ## Monitoring
399
+
400
+ ActionWebPush integrates with ActiveSupport::Notifications for comprehensive monitoring:
401
+
402
+ ```ruby
403
+ # Subscribe to all ActionWebPush events
404
+ ActiveSupport::Notifications.subscribe(/^action_web_push\./) do |name, start, finish, id, payload|
405
+ duration = (finish - start) * 1000
406
+ Rails.logger.info "#{name}: #{duration.round(2)}ms #{payload.inspect}"
407
+ end
408
+
409
+ # Monitor specific events
410
+ ActiveSupport::Notifications.subscribe("action_web_push.notification_delivery") do |name, start, finish, id, payload|
411
+ if payload[:success]
412
+ Metrics.increment('push.delivery.success')
413
+ else
414
+ Metrics.increment('push.delivery.failure', tags: { code: payload[:response_code] })
415
+ end
416
+ end
417
+
418
+ # Track rate limiting
419
+ ActiveSupport::Notifications.subscribe("action_web_push.rate_limit_exceeded") do |name, start, finish, id, payload|
420
+ Metrics.increment('push.rate_limit_exceeded',
421
+ tags: { resource_type: payload[:resource_type] })
422
+ end
423
+
424
+ # Pool overflow monitoring
425
+ ActiveSupport::Notifications.subscribe("action_web_push.pool_overflow") do |name, start, finish, id, payload|
426
+ Metrics.gauge('push.pool.overflow_rate', payload[:overflow_rate])
427
+ end
428
+ ```
429
+
430
+ ### Available Events
431
+
432
+ - `action_web_push.notification_delivery` - Individual notification delivery
433
+ - `action_web_push.subscription_expired` - Subscription marked as expired
434
+ - `action_web_push.rate_limit_exceeded` - Rate limit threshold hit
435
+ - `action_web_push.pool_overflow` - Thread pool queue overflow
436
+ - `action_web_push.notification_delivery_failed` - Delivery failure
437
+ - `action_web_push.configuration_error` - Configuration validation error
438
+ - `action_web_push.unexpected_error` - Unexpected error occurred
439
+
440
+ ## Testing
441
+
442
+ ActionWebPush includes comprehensive test helpers:
443
+
444
+ ```ruby
445
+ # Test mode (add to test environment)
446
+ ActionWebPush.configure do |config|
447
+ config.delivery_method = :test
448
+ end
449
+
450
+ # In your tests
451
+ require 'action_web_push/test_helper'
452
+
453
+ class NotificationTest < ActiveSupport::TestCase
454
+ include ActionWebPush::TestHelper
455
+
456
+ test "sends welcome notification" do
457
+ user = users(:alice)
458
+
459
+ assert_enqueued_push_deliveries 1 do
460
+ UserNotifications.welcome(user).deliver_later
461
+ end
462
+
463
+ assert_push_delivered_to user.push_subscriptions.first do |notification|
464
+ assert_equal "Welcome!", notification[:title]
465
+ assert_equal "welcome", notification[:data][:type]
466
+ end
467
+ end
468
+
469
+ test "handles expired subscriptions" do
470
+ expired_subscription = push_subscriptions(:expired)
471
+
472
+ assert_raises ActionWebPush::ExpiredSubscriptionError do
473
+ notification = expired_subscription.build_notification(
474
+ title: "Test", body: "Test"
475
+ )
476
+ notification.deliver_now
477
+ end
478
+ end
479
+ end
480
+ ```
481
+
482
+ ### Test Helpers
483
+
484
+ - `assert_push_delivered_to(subscription, &block)` - Assert notification delivered
485
+ - `assert_enqueued_push_deliveries(count, &block)` - Assert jobs enqueued
486
+ - `assert_no_push_deliveries(&block)` - Assert no deliveries
487
+ - `clear_push_deliveries` - Clear test delivery queue
488
+
489
+ ## Configuration Reference
490
+
491
+ Complete configuration options:
492
+
493
+ ```ruby
494
+ ActionWebPush.configure do |config|
495
+ # VAPID Configuration (Required)
496
+ config.vapid_subject = "mailto:admin@example.com" # or "https://example.com"
497
+ config.vapid_public_key = "your_public_key"
498
+ config.vapid_private_key = "your_private_key"
499
+
500
+ # Delivery Method
501
+ config.delivery_method = :web_push # :web_push, :test, or custom
502
+
503
+ # Thread Pool Configuration
504
+ config.pool_size = 10 # Max concurrent deliveries
505
+ config.queue_size = 100 # Queue size before overflow
506
+ config.connection_pool_size = 5 # HTTP connection pool size
507
+
508
+ # Batch Processing
509
+ config.batch_size = 100 # Default batch size
510
+
511
+ # Rate Limiting (optional - uses sensible defaults)
512
+ config.rate_limits = {
513
+ endpoint: { max_requests: 100, window: 3600 },
514
+ user: { max_requests: 1000, window: 3600 },
515
+ global: { max_requests: 10000, window: 3600 },
516
+ subscription: { max_requests: 50, window: 3600 }
517
+ }
518
+
519
+ # Logging
520
+ config.logger = Rails.logger # Custom logger
521
+ end
522
+ ```
523
+
524
+ ## API Documentation
525
+
526
+ For detailed API documentation, see [API.md](API.md).
527
+
528
+ ## Requirements
529
+
530
+ - Ruby 3.0+
531
+ - Rails 7.0+
532
+ - Redis (optional, for rate limiting)
533
+
534
+ ## Dependencies
535
+
536
+ - `web-push` - Web Push protocol implementation
537
+ - `concurrent-ruby` - Thread pool management
538
+ - `activejob` - Background job processing
539
+ - `activerecord` - Database integration
540
+
541
+ ## Contributing
542
+
543
+ We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.
544
+
545
+ 1. Fork the repository
546
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
547
+ 3. Write tests for your changes
548
+ 4. Ensure all tests pass (`bundle exec rspec`)
549
+ 5. Commit your changes (`git commit -am 'Add some feature'`)
550
+ 6. Push to the branch (`git push origin my-new-feature`)
551
+ 7. Create a Pull Request
552
+
553
+ ## Changelog
554
+
555
+ See [CHANGELOG.md](CHANGELOG.md) for a list of changes.
556
+
557
+ ## License
558
+
559
+ The gem is available as open source under the terms of the [MIT License](LICENSE).
560
+
561
+ ## Security
562
+
563
+ For security issues, please email security@yourapp.com instead of using the issue tracker.
564
+
565
+ ## Support
566
+
567
+ - GitHub Issues: Report bugs and feature requests
568
+ - Documentation: [API.md](API.md)
569
+ - Examples: See `examples/` directory
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList[
10
+ "test/basic_test.rb",
11
+ "test/comprehensive_test.rb",
12
+ "test/delivery_methods_test.rb"
13
+ ]
14
+ t.verbose = true
15
+ end
16
+
17
+ task default: :test
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionWebPush
4
+ class SubscriptionsController < ActionController::Base
5
+ before_action :authenticate_user!, if: :respond_to_authenticate_user?
6
+ before_action :set_user
7
+ before_action :set_subscription, only: [:show, :destroy]
8
+
9
+ def index
10
+ @subscriptions = ActionWebPush::Subscription.for_user(@user)
11
+ render json: @subscriptions
12
+ end
13
+
14
+ def show
15
+ render json: @subscription
16
+ end
17
+
18
+ def create
19
+ @subscription = ActionWebPush::Subscription.find_or_create_subscription(
20
+ user: @user,
21
+ **subscription_params,
22
+ user_agent: request.user_agent
23
+ )
24
+
25
+ if @subscription.persisted?
26
+ render json: @subscription, status: :created
27
+ else
28
+ render json: { errors: @subscription.errors }, status: :unprocessable_entity
29
+ end
30
+ end
31
+
32
+ def destroy
33
+ @subscription.destroy
34
+ head :no_content
35
+ end
36
+
37
+ private
38
+
39
+ def set_user
40
+ @user = current_user if respond_to?(:current_user)
41
+ @user ||= User.find(params[:user_id]) if params[:user_id]
42
+
43
+ unless @user
44
+ render json: { error: "User not found or not authenticated" }, status: :unauthorized
45
+ end
46
+ end
47
+
48
+ def set_subscription
49
+ @subscription = ActionWebPush::Subscription.for_user(@user).find(params[:id])
50
+ end
51
+
52
+ def subscription_params
53
+ params.require(:subscription).permit(:endpoint, :p256dh_key, :auth_key)
54
+ end
55
+
56
+ def respond_to_authenticate_user?
57
+ respond_to?(:authenticate_user!)
58
+ end
59
+ end
60
+ end